aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--COPYING339
-rw-r--r--README22
-rw-r--r--src/Makefile.am16
-rw-r--r--src/gprs_bssgp_pcu.cpp223
-rw-r--r--src/gprs_bssgp_pcu.h7
-rw-r--r--src/gprs_debug.cpp1
-rw-r--r--src/gprs_debug.h1
-rw-r--r--src/gprs_rlcmac.cpp1177
-rw-r--r--src/gprs_rlcmac.h180
-rw-r--r--src/gprs_rlcmac_data.cpp841
-rw-r--r--src/gprs_rlcmac_sched.cpp336
-rw-r--r--src/openbts_sock.cpp4
-rw-r--r--src/pcu_l1_if.cpp154
-rw-r--r--src/pcu_main.cpp107
-rw-r--r--src/pcu_vty.c313
-rw-r--r--src/pcu_vty.h22
-rw-r--r--src/pcuif_proto.h25
-rw-r--r--src/sysmo_sock.cpp35
-rw-r--r--src/tbf.txt50
19 files changed, 3283 insertions, 570 deletions
diff --git a/COPYING b/COPYING
new file mode 100644
index 00000000..d159169d
--- /dev/null
+++ b/COPYING
@@ -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.
diff --git a/README b/README
new file mode 100644
index 00000000..2407e2f7
--- /dev/null
+++ b/README
@@ -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
diff --git a/src/Makefile.am b/src/Makefile.am
index 67b5b12b..e52a597b 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -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) \
diff --git a/src/gprs_bssgp_pcu.cpp b/src/gprs_bssgp_pcu.cpp
index 8e81304f..44fb7006 100644
--- a/src/gprs_bssgp_pcu.cpp
+++ b/src/gprs_bssgp_pcu.cpp
@@ -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;
diff --git a/src/gprs_bssgp_pcu.h b/src/gprs_bssgp_pcu.h
index 7d5f3767..2d661886 100644
--- a/src/gprs_bssgp_pcu.h
+++ b/src/gprs_bssgp_pcu.h
@@ -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
diff --git a/src/gprs_debug.cpp b/src/gprs_debug.cpp
index bc19b77e..2b9b690b 100644
--- a/src/gprs_debug.cpp
+++ b/src/gprs_debug.cpp
@@ -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},
};
diff --git a/src/gprs_debug.h b/src/gprs_debug.h
index b5b42767..1a5f01a0 100644
--- a/src/gprs_debug.h
+++ b/src/gprs_debug.h
@@ -34,6 +34,7 @@ enum {
DRLCMACDL,
DRLCMACUL,
DRLCMACSCHED,
+ DRLCMACBW,
DBSSGP,
DPCU,
aDebug_LastEntry
diff --git a/src/gprs_rlcmac.cpp b/src/gprs_rlcmac.cpp
index 73531cff..b38722d7 100644
--- a/src/gprs_rlcmac.cpp
+++ b/src/gprs_rlcmac.cpp
@@ -22,18 +22,96 @@
#include <pcu_l1_if.h>
#include <gprs_rlcmac.h>
-LLIST_HEAD(gprs_rlcmac_tbfs);
-void *rlcmac_tall_ctx;
+/* 3GPP TS 05.02 Annex B.1 */
-/* FIXME: spread resources on multiple TRX */
-int tfi_alloc(uint8_t *_trx, uint8_t *_ts)
+#define MS_NA 255 /* N/A */
+#define MS_A 254 /* 1 with hopping, 0 without */
+#define MS_B 253 /* 1 with hopping, 0 without (change Rx to Tx)*/
+#define MS_C 252 /* 1 with hopping, 0 without (change Tx to Rx)*/
+
+struct gprs_ms_multislot_class {
+ uint8_t rx, tx, sum; /* Maximum Number of Slots: RX, Tx, Sum Rx+Tx */
+ uint8_t ta, tb, ra, rb; /* Minimum Number of Slots */
+ uint8_t type; /* Type of Mobile */
+};
+
+struct gprs_ms_multislot_class gprs_ms_multislot_class[32] = {
+/* M-S Class Rx Tx Sum Tta Ttb Tra Trb Type */
+/* N/A */ { MS_NA,MS_NA, MS_NA, MS_NA, MS_NA, MS_NA, MS_NA, MS_NA },
+/* 1 */ { 1, 1, 2, 3, 2, 4, 2, 1 },
+/* 2 */ { 2, 1, 3, 3, 2, 3, 1, 1 },
+/* 3 */ { 2, 2, 3, 3, 2, 3, 1, 1 },
+/* 4 */ { 3, 1, 4, 3, 1, 3, 1, 1 },
+/* 5 */ { 2, 2, 4, 3, 1, 3, 1, 1 },
+/* 6 */ { 3, 2, 4, 3, 1, 3, 1, 1 },
+/* 7 */ { 3, 3, 4, 3, 1, 3, 1, 1 },
+/* 8 */ { 4, 1, 5, 3, 1, 2, 1, 1 },
+/* 9 */ { 3, 2, 5, 3, 1, 2, 1, 1 },
+/* 10 */ { 4, 2, 5, 3, 1, 2, 1, 1 },
+/* 11 */ { 4, 3, 5, 3, 1, 2, 1, 1 },
+/* 12 */ { 4, 4, 5, 2, 1, 2, 1, 1 },
+/* 13 */ { 3, 3, MS_NA, MS_NA, MS_A, 3, MS_A, 2 },
+/* 14 */ { 4, 4, MS_NA, MS_NA, MS_A, 3, MS_A, 2 },
+/* 15 */ { 5, 5, MS_NA, MS_NA, MS_A, 3, MS_A, 2 },
+/* 16 */ { 6, 6, MS_NA, MS_NA, MS_A, 2, MS_A, 2 },
+/* 17 */ { 7, 7, MS_NA, MS_NA, MS_A, 1, 0, 2 },
+/* 18 */ { 8, 8, MS_NA, MS_NA, 0, 0, 0, 2 },
+/* 19 */ { 6, 2, MS_NA, 3, MS_B, 2, MS_C, 1 },
+/* 20 */ { 6, 3, MS_NA, 3, MS_B, 2, MS_C, 1 },
+/* 21 */ { 6, 4, MS_NA, 3, MS_B, 2, MS_C, 1 },
+/* 22 */ { 6, 4, MS_NA, 2, MS_B, 2, MS_C, 1 },
+/* 23 */ { 6, 6, MS_NA, 2, MS_B, 2, MS_C, 1 },
+/* 24 */ { 8, 2, MS_NA, 3, MS_B, 2, MS_C, 1 },
+/* 25 */ { 8, 3, MS_NA, 3, MS_B, 2, MS_C, 1 },
+/* 26 */ { 8, 4, MS_NA, 3, MS_B, 2, MS_C, 1 },
+/* 27 */ { 8, 4, MS_NA, 2, MS_B, 2, MS_C, 1 },
+/* 28 */ { 8, 6, MS_NA, 2, MS_B, 2, MS_C, 1 },
+/* 29 */ { 8, 8, MS_NA, 2, MS_B, 2, MS_C, 1 },
+/* N/A */ { MS_NA,MS_NA, MS_NA, MS_NA, MS_NA, MS_NA, MS_NA, MS_NA },
+/* N/A */ { MS_NA,MS_NA, MS_NA, MS_NA, MS_NA, MS_NA, MS_NA, MS_NA },
+};
+
+struct gprs_rlcmac_cs gprs_rlcmac_cs[] = {
+/* frame length data block max payload */
+ { 0, 0, 0 },
+ { 23, 23, 20 }, /* CS-1 */
+ { 34, 33, 30 }, /* CS-2 */
+ { 40, 39, 36 }, /* CS-3 */
+ { 54, 53, 50 }, /* CS-4 */
+};
+
+LLIST_HEAD(gprs_rlcmac_ul_tbfs);
+LLIST_HEAD(gprs_rlcmac_dl_tbfs);
+llist_head *gprs_rlcmac_tbfs_lists[] = {
+ &gprs_rlcmac_ul_tbfs,
+ &gprs_rlcmac_dl_tbfs,
+ NULL
+};
+extern void *tall_pcu_ctx;
+
+/* FIXME: spread ressources over multiple TRX. Also add option to use same
+ * TRX in case of existing TBF for TLLI in the other direction. */
+/* search for free TFI and return TFI, TRX and first TS */
+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_bts *bts = gprs_rlcmac_bts;
struct gprs_rlcmac_pdch *pdch;
- uint8_t trx, ts, tfi;
+ struct gprs_rlcmac_tbf **tbfp;
+ uint8_t trx_from, trx_to, trx, ts, tfi;
+
+ if (use_trx >= 0 && use_trx < 8)
+ trx_from = trx_to = use_trx;
+ else {
+ trx_from = 0;
+ trx_to = 7;
+ }
+ if (first_ts < 0 || first_ts >= 8)
+ first_ts = 0;
- for (trx = 0; trx < 8; trx++) {
- for (ts = 0; ts < 8; ts++) {
+ /* on TRX find first enabled TS */
+ for (trx = trx_from; trx <= trx_to; trx++) {
+ for (ts = first_ts; ts < 8; ts++) {
pdch = &bts->trx[trx].pdch[ts];
if (!pdch->enable)
continue;
@@ -42,16 +120,20 @@ int tfi_alloc(uint8_t *_trx, uint8_t *_ts)
if (ts < 8)
break;
}
- if (trx == 8) {
+ if (trx > trx_to) {
LOGP(DRLCMAC, LOGL_NOTICE, "No PDCH available.\n");
return -EINVAL;
}
LOGP(DRLCMAC, LOGL_DEBUG, "Searching for first unallocated TFI: "
- "TRX=%d TS=%d\n", trx, ts);
+ "TRX=%d first TS=%d\n", trx, ts);
+ if (dir == GPRS_RLCMAC_UL_TBF)
+ tbfp = bts->trx[trx].ul_tbf;
+ else
+ tbfp = bts->trx[trx].dl_tbf;
for (tfi = 0; tfi < 32; tfi++) {
- if (!pdch->tbf[tfi])
+ if (!tbfp[tfi])
break;
}
@@ -66,131 +148,664 @@ int tfi_alloc(uint8_t *_trx, uint8_t *_ts)
return -1;
}
-int find_free_usf(uint8_t trx, uint8_t ts)
+static inline int8_t find_free_usf(struct gprs_rlcmac_pdch *pdch, uint8_t ts)
{
- struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts;
- struct gprs_rlcmac_pdch *pdch;
struct gprs_rlcmac_tbf *tbf;
uint8_t usf_map = 0;
uint8_t tfi, usf;
- if (trx >= 8 || ts >= 8)
- return -EINVAL;
- pdch = &bts->trx[trx].pdch[ts];
-
/* make map of used USF */
for (tfi = 0; tfi < 32; tfi++) {
- tbf = pdch->tbf[tfi];
+ tbf = pdch->ul_tbf[tfi];
if (!tbf)
continue;
- if (tbf->direction != GPRS_RLCMAC_UL_TBF)
- continue;
- usf_map |= (1 << tbf->dir.ul.usf);
+ usf_map |= (1 << tbf->dir.ul.usf[ts]);
}
/* look for USF, don't use USF=7 */
for (usf = 0; usf < 7; usf++) {
- if (!(usf_map & (1 << usf))) {
- LOGP(DRLCMAC, LOGL_DEBUG, " Found USF=%d.\n", usf);
+ if (!(usf_map & (1 << usf)))
return usf;
- }
}
- LOGP(DRLCMAC, LOGL_NOTICE, "No USF available.\n");
return -1;
}
/* lookup TBF Entity (by TFI) */
-#warning FIXME: use pdch instance by trx and ts, because tfi is local
-struct gprs_rlcmac_tbf *tbf_by_tfi(uint8_t tfi, int direction)
+struct gprs_rlcmac_tbf *tbf_by_tfi(uint8_t tfi, uint8_t trx,
+ enum gprs_rlcmac_tbf_direction dir)
{
struct gprs_rlcmac_tbf *tbf;
+ struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts;
- llist_for_each_entry(tbf, &gprs_rlcmac_tbfs, list) {
- if (tbf->state != GPRS_RLCMAC_RELEASING
- && tbf->tfi == tfi
- && tbf->direction == direction)
+ if (tfi >= 32 || trx >= 8)
+ return NULL;
+
+ if (dir == GPRS_RLCMAC_UL_TBF)
+ tbf = bts->trx[trx].ul_tbf[tfi];
+ else
+ tbf = bts->trx[trx].dl_tbf[tfi];
+ if (!tbf)
+ return NULL;
+
+ if (tbf->state != GPRS_RLCMAC_RELEASING)
return tbf;
- }
+
return NULL;
}
/* search for active downlink or uplink tbf */
-struct gprs_rlcmac_tbf *tbf_by_tlli(uint32_t tlli, int direction)
+struct gprs_rlcmac_tbf *tbf_by_tlli(uint32_t tlli,
+ enum gprs_rlcmac_tbf_direction dir)
{
struct gprs_rlcmac_tbf *tbf;
- llist_for_each_entry(tbf, &gprs_rlcmac_tbfs, list) {
- if (tbf->state != GPRS_RLCMAC_RELEASING
- && tbf->tlli == tlli
- && tbf->direction == direction)
- return tbf;
+ if (dir == GPRS_RLCMAC_UL_TBF) {
+ llist_for_each_entry(tbf, &gprs_rlcmac_ul_tbfs, list) {
+ if (tbf->state != GPRS_RLCMAC_RELEASING
+ && tbf->tlli == tlli && tbf->tlli_valid)
+ return tbf;
+ }
+ } else {
+ llist_for_each_entry(tbf, &gprs_rlcmac_dl_tbfs, list) {
+ if (tbf->state != GPRS_RLCMAC_RELEASING
+ && tbf->tlli == tlli)
+ return tbf;
+ }
}
return NULL;
}
-#warning FIXME: use pdch instance by trx and ts, because polling is local
-struct gprs_rlcmac_tbf *tbf_by_poll_fn(uint32_t fn)
+struct gprs_rlcmac_tbf *tbf_by_poll_fn(uint32_t fn, uint8_t trx, uint8_t ts)
{
struct gprs_rlcmac_tbf *tbf;
- llist_for_each_entry(tbf, &gprs_rlcmac_tbfs, list) {
+
+ /* only one TBF can poll on specific TS/FN, because scheduler can only
+ * schedule one downlink control block (with polling) at a FN per TS */
+ llist_for_each_entry(tbf, &gprs_rlcmac_ul_tbfs, list) {
+ if (tbf->state != GPRS_RLCMAC_RELEASING
+ && tbf->poll_state == GPRS_RLCMAC_POLL_SCHED
+ && tbf->poll_fn == fn && tbf->trx == trx
+ && tbf->control_ts == ts)
+ return tbf;
+ }
+ llist_for_each_entry(tbf, &gprs_rlcmac_dl_tbfs, list) {
if (tbf->state != GPRS_RLCMAC_RELEASING
&& tbf->poll_state == GPRS_RLCMAC_POLL_SCHED
- && tbf->poll_fn == fn)
+ && tbf->poll_fn == fn && tbf->trx == trx
+ && tbf->control_ts == ts)
return tbf;
}
return NULL;
}
-struct gprs_rlcmac_tbf *tbf_alloc(uint8_t tfi, uint8_t trx, uint8_t 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_bts *bts = gprs_rlcmac_bts;
- struct gprs_rlcmac_pdch *pdch;
struct gprs_rlcmac_tbf *tbf;
+ int rc;
LOGP(DRLCMAC, LOGL_DEBUG, "********** TBF starts here **********\n");
- LOGP(DRLCMAC, LOGL_INFO, "Allocating TBF with TFI=%d.\n", tfi);
+ LOGP(DRLCMAC, LOGL_INFO, "Allocating %s TBF: TFI=%d TRX=%d "
+ "MS_CLASS=%d\n", (dir == GPRS_RLCMAC_UL_TBF) ? "UL" : "DL",
+ tfi, trx, ms_class);
- if (trx >= 8 || ts >= 8 || tfi >= 32)
+ if (trx >= 8 || first_ts >= 8 || tfi >= 32)
return NULL;
- pdch = &bts->trx[trx].pdch[ts];
- tbf = talloc_zero(rlcmac_tall_ctx, struct gprs_rlcmac_tbf);
+ tbf = talloc_zero(tall_pcu_ctx, struct gprs_rlcmac_tbf);
if (!tbf)
return NULL;
+ tbf->direction = dir;
tbf->tfi = tfi;
tbf->trx = trx;
- tbf->ts = ts;
tbf->arfcn = bts->trx[trx].arfcn;
- tbf->tsc = bts->trx[trx].pdch[ts].tsc;
- tbf->pdch = pdch;
+ tbf->first_ts = first_ts;
+ tbf->ms_class = ms_class;
tbf->ws = 64;
tbf->sns = 128;
+ /* select algorithm A in case we don't have multislot class info */
+ if (single_slot || ms_class == 0)
+ rc = alloc_algorithm_a(old_tbf, tbf,
+ bts->alloc_algorithm_curst);
+ else
+ rc = bts->alloc_algorithm(old_tbf, tbf,
+ bts->alloc_algorithm_curst);
+ /* if no ressource */
+ if (rc < 0) {
+ talloc_free(tbf);
+ return NULL;
+ }
+ /* assign control ts */
+ tbf->control_ts = 0xff;
+ rc = tbf_assign_control_ts(tbf);
+ /* if no ressource */
+ if (rc < 0) {
+ talloc_free(tbf);
+ return NULL;
+ }
+
+ /* set timestamp */
+ gettimeofday(&tbf->bw_tv, NULL);
+
INIT_LLIST_HEAD(&tbf->llc_queue);
- llist_add(&tbf->list, &gprs_rlcmac_tbfs);
- pdch->tbf[tfi] = tbf;
+ if (dir == GPRS_RLCMAC_UL_TBF)
+ llist_add(&tbf->list, &gprs_rlcmac_ul_tbfs);
+ else
+ llist_add(&tbf->list, &gprs_rlcmac_dl_tbfs);
return tbf;
}
-void tbf_free(struct gprs_rlcmac_tbf *tbf)
+/* Slot Allocation: Algorithm A
+ *
+ * Assign single slot for uplink and downlink
+ */
+int alloc_algorithm_a(struct gprs_rlcmac_tbf *old_tbf,
+ struct gprs_rlcmac_tbf *tbf, uint32_t cust)
+{
+ struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts;
+ struct gprs_rlcmac_pdch *pdch;
+ uint8_t ts = tbf->first_ts;
+ int8_t usf; /* must be signed */
+
+ LOGP(DRLCMAC, LOGL_DEBUG, "Slot Allocation (Algorithm A) for class "
+ "%d\n", tbf->ms_class);
+
+ pdch = &bts->trx[tbf->trx].pdch[ts];
+ if (!pdch->enable) {
+ LOGP(DRLCMAC, LOGL_ERROR, "TS=%d not enabled.", ts);
+ return -EIO;
+ }
+ tbf->tsc = pdch->tsc;
+ if (tbf->direction == GPRS_RLCMAC_UL_TBF) {
+ /* if USF available */
+ usf = find_free_usf(pdch, ts);
+ if (usf >= 0) {
+ LOGP(DRLCMAC, LOGL_DEBUG, "- Assign uplink "
+ "TS=%d USF=%d\n", ts, usf);
+ bts->trx[tbf->trx].ul_tbf[tbf->tfi] = tbf;
+ pdch->ul_tbf[tbf->tfi] = tbf;
+ tbf->pdch[ts] = pdch;
+ } else {
+ LOGP(DRLCMAC, LOGL_NOTICE, "- Failed "
+ "allocating TS=%d, no USF available\n", ts);
+ return -EBUSY;
+ }
+ } else {
+ LOGP(DRLCMAC, LOGL_DEBUG, "- Assign downlink TS=%d\n", ts);
+ bts->trx[tbf->trx].dl_tbf[tbf->tfi] = tbf;
+ pdch->dl_tbf[tbf->tfi] = tbf;
+ tbf->pdch[ts] = pdch;
+ }
+ /* the only one TS is the common TS */
+ tbf->first_common_ts = ts;
+
+ return 0;
+}
+
+/* Slot Allocation: Algorithm B
+ *
+ * Assign as many downlink slots as possible.
+ * Assign one uplink slot. (With free USF)
+ *
+ */
+int alloc_algorithm_b(struct gprs_rlcmac_tbf *old_tbf,
+ struct gprs_rlcmac_tbf *tbf, uint32_t cust)
{
struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts;
struct gprs_rlcmac_pdch *pdch;
+ struct gprs_ms_multislot_class *ms_class;
+ uint8_t Rx, Tx, Sum; /* Maximum Number of Slots: RX, Tx, Sum Rx+Tx */
+ uint8_t Tta, Ttb, Tra, Trb, Tt, Tr; /* Minimum Number of Slots */
+ uint8_t Type; /* Type of Mobile */
+ uint8_t rx_win_min, rx_win_max;
+ uint8_t tx_win_min, tx_win_max, tx_range;
+ uint8_t rx_window = 0, tx_window = 0;
+ const char *digit[10] = { "0","1","2","3","4","5","6","7","8","9" };
+ int8_t usf[8] = { -1, -1, -1, -1, -1, -1, -1, -1 }; /* must be signed */
+ int8_t tsc = -1; /* must be signed */
+ uint8_t i, ts;
+
+ LOGP(DRLCMAC, LOGL_DEBUG, "Slot Allocation (Algorithm B) for class "
+ "%d\n", tbf->ms_class);
+
+ if (tbf->ms_class >= 32) {
+ LOGP(DRLCMAC, LOGL_ERROR, "Multislot class %d out of range.\n",
+ tbf->ms_class);
+ return -EINVAL;
+ }
+
+ ms_class = &gprs_ms_multislot_class[tbf->ms_class];
+ if (ms_class->tx == MS_NA) {
+ LOGP(DRLCMAC, LOGL_NOTICE, "Multislot class %d not "
+ "applicable.\n", tbf->ms_class);
+ return -EINVAL;
+ }
+
+ Rx = ms_class->rx;
+ Tx = ms_class->tx;
+ Sum = ms_class->sum;
+ Tta = ms_class->ta;
+ Ttb = ms_class->tb;
+ Tra = ms_class->ra;
+ Trb = ms_class->rb;
+ Type = ms_class->type;
+
+ /* Tta and Ttb may depend on hopping or frequency change */
+ if (Ttb == MS_A) {
+ if (/* FIXME: hopping*/ 0)
+ Ttb = 1;
+ else
+ Ttb = 0;
+ }
+ if (Trb == MS_A) {
+ if (/* FIXME: hopping*/ 0)
+ Ttb = 1;
+ else
+ Ttb = 0;
+ }
+ if (Ttb == MS_B) {
+ /* FIXME: or frequency change */
+ if (/* FIXME: hopping*/ 0)
+ Ttb = 1;
+ else
+ Ttb = 0;
+ }
+ if (Trb == MS_C) {
+ /* FIXME: or frequency change */
+ if (/* FIXME: hopping*/ 0)
+ Ttb = 1;
+ else
+ Ttb = 0;
+ }
+
+ LOGP(DRLCMAC, LOGL_DEBUG, "- Rx=%d Tx=%d Sum Rx+Tx=%s Tta=%s Ttb=%d "
+ " Tra=%d Trb=%d Type=%d\n", Rx, Tx,
+ (Sum == MS_NA) ? "N/A" : digit[Sum],
+ (Tta == MS_NA) ? "N/A" : digit[Tta], Ttb, Tra, Trb, Type);
+
+ /* select the values for time contraints */
+ if (/* FIXME: monitoring */0) {
+ /* applicable to type 1 and type 2 */
+ Tt = Ttb;
+ Tr = Tra;
+ } else {
+ /* applicable to type 1 and type 2 */
+ Tt = Ttb;
+ Tr = Trb;
+ }
+
+ /* select a window of Rx slots if available
+ * The maximum allowed slots depend on RX or the window of available
+ * slots.
+ * This must be done for uplink TBF also, because it is the basis
+ * for calculating control slot and uplink slot(s). */
+ rx_win_min = rx_win_max = tbf->first_ts;
+ for (ts = tbf->first_ts, i = 0; ts < 8; ts++) {
+ pdch = &bts->trx[tbf->trx].pdch[ts];
+ /* check if enabled */
+ if (!pdch->enable) {
+ LOGP(DRLCMAC, LOGL_DEBUG, "- Skipping TS %d, because "
+ "not enabled\n", ts);
+ /* increase window for Type 1 */
+ if (Type == 1)
+ i++;
+ continue;
+ }
+ /* check if TSC changes */
+ if (tsc < 0)
+ tbf->tsc = tsc = pdch->tsc;
+ else if (tsc != pdch->tsc) {
+ LOGP(DRLCMAC, LOGL_ERROR, "Skipping TS %d of TRX=%d, "
+ "because it has different TSC than lower TS "
+ "of TRX. In order to allow multislot, all "
+ "slots must be configured with the same "
+ "TSC!\n", ts, tbf->trx);
+ /* increase window for Type 1 */
+ if (Type == 1)
+ i++;
+ continue;
+ }
+
+ rx_window |= (1 << ts);
+ LOGP(DRLCMAC, LOGL_DEBUG, "- Selected DL TS %d\n", ts);
+
+ /* range of window (required for Type 1) */
+ rx_win_max = ts;
+
+ if (++i == Rx) {
+ LOGP(DRLCMAC, LOGL_DEBUG, "- Done, because slots / "
+ "window reached maximum alowed Rx size\n");
+ break;
+ }
+ }
+
+ LOGP(DRLCMAC, LOGL_DEBUG, "- Selected slots for RX: "
+ "(TS=0)\"%c%c%c%c%c%c%c%c\"(TS=7)\n",
+ ((rx_window & 0x01)) ? 'D' : '.',
+ ((rx_window & 0x02)) ? 'D' : '.',
+ ((rx_window & 0x04)) ? 'D' : '.',
+ ((rx_window & 0x08)) ? 'D' : '.',
+ ((rx_window & 0x10)) ? 'D' : '.',
+ ((rx_window & 0x20)) ? 'D' : '.',
+ ((rx_window & 0x40)) ? 'D' : '.',
+ ((rx_window & 0x80)) ? 'D' : '.');
+
+ /* reduce window, if existing uplink slots collide RX window */
+ if (Type == 1 && old_tbf && old_tbf->direction == GPRS_RLCMAC_UL_TBF) {
+ uint8_t collide = 0, ul_usage = 0;
+ int j;
+
+ /* calculate mask of colliding slots */
+ for (ts = old_tbf->first_ts; ts < 8; ts++) {
+ if (old_tbf->pdch[ts]) {
+ ul_usage |= (1 << ts);
+ /* mark bits from TS-t .. TS+r */
+ for (j = ts - Tt; j != ((ts + Tr + 1) & 7);
+ j = (j + 1) & 7)
+ collide |= (1 << j);
+ }
+ }
+ LOGP(DRLCMAC, LOGL_DEBUG, "- Not allowed slots due to existing "
+ "UL allocation: (TS=0)\"%c%c%c%c%c%c%c%c\"(TS=7) "
+ " D=downlink x=not usable\n",
+ ((ul_usage & 0x01)) ? 'D' : ((collide & 0x01))?'x':'.',
+ ((ul_usage & 0x02)) ? 'D' : ((collide & 0x02))?'x':'.',
+ ((ul_usage & 0x04)) ? 'D' : ((collide & 0x04))?'x':'.',
+ ((ul_usage & 0x08)) ? 'D' : ((collide & 0x08))?'x':'.',
+ ((ul_usage & 0x10)) ? 'D' : ((collide & 0x10))?'x':'.',
+ ((ul_usage & 0x20)) ? 'D' : ((collide & 0x20))?'x':'.',
+ ((ul_usage & 0x40)) ? 'D' : ((collide & 0x40))?'x':'.',
+ ((ul_usage & 0x80)) ? 'D' : ((collide & 0x80))?'x':'.');
+
+ /* apply massk to reduce tx_window (shifted by 3 slots) */
+ rx_window &= ~(collide << 3);
+ rx_window &= ~(collide >> 5);
+ LOGP(DRLCMAC, LOGL_DEBUG, "- Remaining slots for RX: "
+ "(TS=0)\"%c%c%c%c%c%c%c%c\"(TS=7)\n",
+ ((rx_window & 0x01)) ? 'D' : '.',
+ ((rx_window & 0x02)) ? 'D' : '.',
+ ((rx_window & 0x04)) ? 'D' : '.',
+ ((rx_window & 0x08)) ? 'D' : '.',
+ ((rx_window & 0x10)) ? 'D' : '.',
+ ((rx_window & 0x20)) ? 'D' : '.',
+ ((rx_window & 0x40)) ? 'D' : '.',
+ ((rx_window & 0x80)) ? 'D' : '.');
+ if (!rx_window) {
+ LOGP(DRLCMAC, LOGL_NOTICE, "No suitable downlink slots "
+ "available with current uplink assignment\n");
+ return -EBUSY;
+ }
+
+ /* calculate new min/max */
+ for (ts = rx_win_min; ts <= rx_win_max; ts++) {
+ if ((rx_window & (1 << ts)))
+ break;
+ rx_win_min = ts + 1;
+ LOGP(DRLCMAC, LOGL_DEBUG, "- TS has been deleted, so "
+ "raising start of DL window to %d\n",
+ rx_win_min);
+ }
+ for (ts = rx_win_max; ts >= rx_win_min; ts--) {
+ if ((rx_window & (1 << ts)))
+ break;
+ rx_win_max = ts - 1;
+ LOGP(DRLCMAC, LOGL_DEBUG, "- TS has been deleted, so "
+ "lowering end of DL window to %d\n",
+ rx_win_max);
+ }
+ }
+
+ /* reduce window, to allow at least one uplink TX slot
+ * this is only required for Type 1 */
+ if (Type == 1 && rx_win_max - rx_win_min + 1 + Tt + 1 + Tr > 8) {
+ rx_win_max = rx_win_min + 7 - Tr - 1 - Tr;
+ LOGP(DRLCMAC, LOGL_DEBUG, "- Reduce RX window due to time "
+ "contraints to %d slots\n",
+ rx_win_max - rx_win_min + 1);
+ }
+
+ LOGP(DRLCMAC, LOGL_DEBUG, "- RX-Window is: %d..%d\n", rx_win_min,
+ rx_win_max);
+
+ /* calculate TX window */
+ if (Type == 1) {
+ /* calculate TX window (shifted by 3 timeslots)
+ * it uses the space between tx_win_max and tx_win_min */
+ tx_win_min = (rx_win_max - 2 + Tt) & 7;
+ tx_win_max = (rx_win_min + 4 - Tr) & 7;
+ /* calculate the TX window size (might be larger than Tx) */
+ tx_range = (tx_win_max - tx_win_min + 1) & 7;
+ } else {
+ /* TX and RX simultaniously */
+ tx_win_min = rx_win_min;
+ tx_win_max = 7;
+ /* TX window size (might be larger than Tx) */
+ tx_range = tx_win_max - tx_win_min + 1;
+ }
+
+ LOGP(DRLCMAC, LOGL_DEBUG, "- TX-Window is: %d..%d\n", tx_win_min,
+ tx_win_max);
+
+ /* select a window of Tx slots if available
+ * The maximum allowed slots depend on TX or the window of available
+ * slots. */
+ if (tbf->direction == GPRS_RLCMAC_UL_TBF) {
+ for (ts = tx_win_min, i = 0; i < tx_range; ts = (ts + 1) & 7) {
+ pdch = &bts->trx[tbf->trx].pdch[ts];
+ /* check if enabled */
+ if (!pdch->enable) {
+ LOGP(DRLCMAC, LOGL_DEBUG, "- Skipping TS %d, "
+ "because not enabled\n", ts);
+ continue;
+ }
+ /* check if TSC changes */
+ if (tsc < 0)
+ tbf->tsc = tsc = pdch->tsc;
+ else if (tsc != pdch->tsc) {
+ LOGP(DRLCMAC, LOGL_ERROR, "Skipping TS %d of "
+ "TRX=%d, because it has different TSC "
+ "than lower TS of TRX. In order to "
+ "allow multislot, all slots must be "
+ "configured with the same TSC!\n",
+ ts, tbf->trx);
+ /* increase window for Type 1 */
+ if (Type == 1)
+ i++;
+ continue;
+ }
+ /* check for free usf */
+ usf[ts] = find_free_usf(pdch, ts);
+ if (usf[ts] < 0) {
+ LOGP(DRLCMAC, LOGL_DEBUG, "- Skipping TS %d, "
+ "because no USF available\n", ts);
+ /* increase window for Type 1 */
+ if (Type == 1)
+ i++;
+ continue;
+ }
+
+ tx_window |= (1 << ts);
+ LOGP(DRLCMAC, LOGL_DEBUG, "- Selected UL TS %d\n", ts);
+
+ if (!(cust & 1)) {
+ LOGP(DRLCMAC, LOGL_DEBUG, "- Done, because "
+ "1 slot assigned\n");
+ break;
+ }
+ if (++i == Tx) {
+ LOGP(DRLCMAC, LOGL_DEBUG, "- Done, because "
+ "slots / window reached maximum alowed "
+ "Tx size\n");
+ break;
+ }
+ }
+
+ LOGP(DRLCMAC, LOGL_DEBUG, "- Selected TX window: "
+ "(TS=0)\"%c%c%c%c%c%c%c%c\"(TS=7)\n",
+ ((tx_window & 0x01)) ? 'U' : '.',
+ ((tx_window & 0x02)) ? 'U' : '.',
+ ((tx_window & 0x04)) ? 'U' : '.',
+ ((tx_window & 0x08)) ? 'U' : '.',
+ ((tx_window & 0x10)) ? 'U' : '.',
+ ((tx_window & 0x20)) ? 'U' : '.',
+ ((tx_window & 0x40)) ? 'U' : '.',
+ ((tx_window & 0x80)) ? 'U' : '.');
+
+ if (!tx_window) {
+ LOGP(DRLCMAC, LOGL_NOTICE, "No suitable uplink slots "
+ "available\n");
+ return -EBUSY;
+ }
+ }
+
+ if (tbf->direction == GPRS_RLCMAC_DL_TBF) {
+ uint8_t slotcount = 0;
+
+ /* assign downlink */
+ if (rx_window == 0) {
+ LOGP(DRLCMAC, LOGL_NOTICE, "No downlink slots "
+ "available\n");
+ return -EINVAL;
+ }
+ for (ts = 0; ts < 8; ts++) {
+ if ((rx_window & (1 << ts))) {
+ LOGP(DRLCMAC, LOGL_DEBUG, "- Assigning DL TS "
+ "%d\n", ts);
+ pdch = &bts->trx[tbf->trx].pdch[ts];
+ bts->trx[tbf->trx].dl_tbf[tbf->tfi] = tbf;
+ pdch->dl_tbf[tbf->tfi] = tbf;
+ tbf->pdch[ts] = pdch;
+ slotcount++;
+ }
+ }
+ if (slotcount)
+ LOGP(DRLCMAC, LOGL_INFO, "Using Multislot with %d "
+ "slots DL\n", slotcount);
+ } else {
+ /* assign uplink */
+ if (tx_window == 0) {
+ LOGP(DRLCMAC, LOGL_NOTICE, "No uplink slots "
+ "available\n");
+ return -EINVAL;
+ }
+ for (ts = 0; ts < 8; ts++) {
+ if ((tx_window & (1 << ts))) {
+ LOGP(DRLCMAC, LOGL_DEBUG, "- Assigning UL TS "
+ "%d\n", ts);
+ pdch = &bts->trx[tbf->trx].pdch[ts];
+ bts->trx[tbf->trx].ul_tbf[tbf->tfi] = tbf;
+ pdch->ul_tbf[tbf->tfi] = tbf;
+ tbf->pdch[ts] = pdch;
+ tbf->dir.ul.usf[ts] = usf[ts];
+ }
+ }
+ }
+
+ /* the timeslot of the TX window start is always
+ * available in RX window */
+ tbf->first_common_ts = tx_win_min;
+
+ return 0;
+}
+
+static void tbf_unlink_pdch(struct gprs_rlcmac_tbf *tbf)
+{
+ struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts;
+ struct gprs_rlcmac_pdch *pdch;
+ int ts;
+
+ if (tbf->direction == GPRS_RLCMAC_UL_TBF) {
+ bts->trx[tbf->trx].ul_tbf[tbf->tfi] = NULL;
+ for (ts = 0; ts < 8; ts++) {
+ pdch = tbf->pdch[ts];
+ if (pdch)
+ pdch->ul_tbf[tbf->tfi] = NULL;
+ tbf->pdch[ts] = NULL;
+ }
+ } else {
+ bts->trx[tbf->trx].dl_tbf[tbf->tfi] = NULL;
+ for (ts = 0; ts < 8; ts++) {
+ pdch = tbf->pdch[ts];
+ if (pdch)
+ pdch->dl_tbf[tbf->tfi] = NULL;
+ tbf->pdch[ts] = NULL;
+ }
+ }
+}
+
+void tbf_free(struct gprs_rlcmac_tbf *tbf)
+{
struct msgb *msg;
- LOGP(DRLCMAC, LOGL_INFO, "Free TBF=%d with TLLI=0x%08x.\n", tbf->tfi,
+ LOGP(DRLCMAC, LOGL_INFO, "Free %s TBF=%d with TLLI=0x%08x.\n",
+ (tbf->direction == GPRS_RLCMAC_UL_TBF) ? "UL" : "DL", tbf->tfi,
tbf->tlli);
+ if (tbf->ul_ass_state != GPRS_RLCMAC_UL_ASS_NONE)
+ LOGP(DRLCMAC, LOGL_ERROR, "Software error: Pending uplink "
+ "assignment. This may not happen, because the "
+ "assignment message never gets transmitted. Please "
+ "be shure not to free in this state. PLEASE FIX!\n");
+ if (tbf->dl_ass_state != GPRS_RLCMAC_DL_ASS_NONE)
+ LOGP(DRLCMAC, LOGL_ERROR, "Software error: Pending downlink "
+ "assignment. This may not happen, because the "
+ "assignment message never gets transmitted. Please "
+ "be shure not to free in this state. PLEASE FIX!\n");
tbf_timer_stop(tbf);
while ((msg = msgb_dequeue(&tbf->llc_queue)))
msgb_free(msg);
- pdch = &bts->trx[tbf->trx].pdch[tbf->ts];
- pdch->tbf[tbf->tfi] = NULL;
+ tbf_unlink_pdch(tbf);
llist_del(&tbf->list);
LOGP(DRLCMAC, LOGL_DEBUG, "********** TBF ends here **********\n");
talloc_free(tbf);
}
+int tbf_update(struct gprs_rlcmac_tbf *tbf)
+{
+ struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts;
+ struct gprs_rlcmac_tbf *ul_tbf = NULL;
+ int rc;
+
+ LOGP(DRLCMAC, LOGL_DEBUG, "********** TBF update **********\n");
+
+ if (tbf->direction != GPRS_RLCMAC_DL_TBF)
+ return -EINVAL;
+
+ if (!tbf->ms_class) {
+ LOGP(DRLCMAC, LOGL_DEBUG, "- Cannot update, no class\n");
+ return -EINVAL;
+ }
+
+ ul_tbf = tbf_by_tlli(tbf->tlli, GPRS_RLCMAC_UL_TBF);
+
+ tbf_unlink_pdch(tbf);
+ rc = bts->alloc_algorithm(ul_tbf, tbf, bts->alloc_algorithm_curst);
+ /* if no ressource */
+ if (rc < 0) {
+ LOGP(DRLCMAC, LOGL_ERROR, "No ressource after update???\n");
+ return -rc;
+ }
+
+ return 0;
+}
+
+int tbf_assign_control_ts(struct gprs_rlcmac_tbf *tbf)
+{
+ if (tbf->control_ts == 0xff)
+ LOGP(DRLCMAC, LOGL_DEBUG, "- Setting Control TS %d\n",
+ tbf->first_common_ts);
+ else if (tbf->control_ts != tbf->first_common_ts)
+ LOGP(DRLCMAC, LOGL_DEBUG, "- Changing Control TS %d\n",
+ tbf->first_common_ts);
+ tbf->control_ts = tbf->first_common_ts;
+
+ return 0;
+}
+
+
const char *tbf_state_name[] = {
"NULL",
"ASSIGN",
@@ -203,8 +818,9 @@ const char *tbf_state_name[] = {
void tbf_new_state(struct gprs_rlcmac_tbf *tbf,
enum gprs_rlcmac_tbf_state state)
{
- LOGP(DRLCMAC, LOGL_DEBUG, "TBF=%d changes state from %s to %s\n",
- tbf->tfi, tbf_state_name[tbf->state], tbf_state_name[state]);
+ LOGP(DRLCMAC, LOGL_DEBUG, "%s TBF=%d changes state from %s to %s\n",
+ (tbf->direction == GPRS_RLCMAC_UL_TBF) ? "UL" : "DL", tbf->tfi,
+ tbf_state_name[tbf->state], tbf_state_name[state]);
tbf->state = state;
}
@@ -212,11 +828,14 @@ void tbf_timer_start(struct gprs_rlcmac_tbf *tbf, unsigned int T,
unsigned int seconds, unsigned int microseconds)
{
if (!osmo_timer_pending(&tbf->timer))
- LOGP(DRLCMAC, LOGL_DEBUG, "Starting TBF=%d timer %u.\n",
+ LOGP(DRLCMAC, LOGL_DEBUG, "Starting %s TBF=%d timer %u.\n",
+ (tbf->direction == GPRS_RLCMAC_UL_TBF) ? "UL" : "DL",
tbf->tfi, T);
else
- LOGP(DRLCMAC, LOGL_DEBUG, "Restarting TBF=%d timer %u while "
- "old timer %u pending \n", tbf->tfi, T, tbf->T);
+ LOGP(DRLCMAC, LOGL_DEBUG, "Restarting %s TBF=%d timer %u "
+ "while old timer %u pending \n",
+ (tbf->direction == GPRS_RLCMAC_UL_TBF) ? "UL" : "DL",
+ tbf->tfi, T, tbf->T);
tbf->T = T;
tbf->num_T_exp = 0;
@@ -231,12 +850,74 @@ void tbf_timer_start(struct gprs_rlcmac_tbf *tbf, unsigned int T,
void tbf_timer_stop(struct gprs_rlcmac_tbf *tbf)
{
if (osmo_timer_pending(&tbf->timer)) {
- LOGP(DRLCMAC, LOGL_DEBUG, "Stopping TBF=%d timer %u.\n",
+ LOGP(DRLCMAC, LOGL_DEBUG, "Stopping %s TBF=%d timer %u.\n",
+ (tbf->direction == GPRS_RLCMAC_UL_TBF) ? "UL" : "DL",
tbf->tfi, tbf->T);
osmo_timer_del(&tbf->timer);
}
}
+/* starting time for assigning single slot
+ * This offset must be a multiple of 13. */
+#define AGCH_START_OFFSET 52
+
+LLIST_HEAD(gprs_rlcmac_sbas);
+
+int sba_alloc(uint8_t *_trx, uint8_t *_ts, uint32_t *_fn, uint8_t ta)
+{
+
+ struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts;
+ struct gprs_rlcmac_pdch *pdch;
+ struct gprs_rlcmac_sba *sba;
+ uint8_t trx, ts;
+ uint32_t fn;
+
+ sba = talloc_zero(tall_pcu_ctx, struct gprs_rlcmac_sba);
+ if (!sba)
+ return -ENOMEM;
+
+ for (trx = 0; trx < 8; trx++) {
+ for (ts = 0; ts < 8; ts++) {
+ pdch = &bts->trx[trx].pdch[ts];
+ if (!pdch->enable)
+ continue;
+ break;
+ }
+ if (ts < 8)
+ break;
+ }
+ if (trx == 8) {
+ LOGP(DRLCMAC, LOGL_NOTICE, "No PDCH available.\n");
+ return -EINVAL;
+ }
+
+ fn = (pdch->last_rts_fn + AGCH_START_OFFSET) % 2715648;
+
+ sba->trx = trx;
+ sba->ts = ts;
+ sba->fn = fn;
+ sba->ta = ta;
+
+ llist_add(&sba->list, &gprs_rlcmac_sbas);
+
+ *_trx = trx;
+ *_ts = ts;
+ *_fn = fn;
+ return 0;
+}
+
+struct gprs_rlcmac_sba *sba_find(uint8_t trx, uint8_t ts, uint32_t fn)
+{
+ struct gprs_rlcmac_sba *sba;
+
+ llist_for_each_entry(sba, &gprs_rlcmac_sbas, list) {
+ if (sba->trx == trx && sba->ts == ts && sba->fn == fn)
+ return sba;
+ }
+
+ return NULL;
+}
+
#if 0
static void tbf_gsm_timer_cb(void *_tbf)
{
@@ -286,7 +967,8 @@ void gprs_rlcmac_enqueue_block(bitvec *block, int len)
#endif
/* received RLC/MAC block from L1 */
-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)
{
unsigned payload = data[0] >> 6;
bitvec *block;
@@ -294,14 +976,15 @@ int gprs_rlcmac_rcv_block(uint8_t *data, uint8_t len, uint32_t fn)
switch (payload) {
case GPRS_RLCMAC_DATA_BLOCK:
- rc = gprs_rlcmac_rcv_data_block_acknowledged(data, len);
+ rc = gprs_rlcmac_rcv_data_block_acknowledged(trx, ts, data,
+ len);
break;
case GPRS_RLCMAC_CONTROL_BLOCK:
block = bitvec_alloc(len);
if (!block)
return -ENOMEM;
bitvec_unpack(block, data);
- rc = gprs_rlcmac_rcv_control_block(block, fn);
+ rc = gprs_rlcmac_rcv_control_block(block, trx, ts, fn);
bitvec_free(block);
break;
case GPRS_RLCMAC_CONTROL_BLOCK_OPT:
@@ -314,11 +997,181 @@ int gprs_rlcmac_rcv_block(uint8_t *data, uint8_t len, uint32_t fn)
return rc;
}
+/* add paging to paging queue(s) */
+int gprs_rlcmac_add_paging(uint8_t chan_needed, uint8_t *identity_lv)
+{
+ struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts;
+ uint8_t l, trx, ts, any_tbf = 0;
+ struct gprs_rlcmac_tbf *tbf;
+ struct gprs_rlcmac_paging *pag;
+ uint8_t slot_mask[8];
+ int8_t first_ts; /* must be signed */
+
+ LOGP(DRLCMAC, LOGL_INFO, "Add RR paging: chan-needed=%d MI=%s\n",
+ chan_needed, osmo_hexdump(identity_lv + 1, identity_lv[0]));
+
+ /* collect slots to page
+ * Mark slots for every TBF, but only mark one of it.
+ * Mark only the first slot found.
+ * Don't mark, if TBF uses a different slot that is already marked. */
+ memset(slot_mask, 0, sizeof(slot_mask));
+ for (l = 0; gprs_rlcmac_tbfs_lists[l]; l++) {
+ llist_for_each_entry(tbf, gprs_rlcmac_tbfs_lists[l], list) {
+ first_ts = -1;
+ for (ts = 0; ts < 8; ts++) {
+ if (tbf->pdch[ts]) {
+ /* remember the first slot found */
+ if (first_ts < 0)
+ first_ts = ts;
+ /* break, if we already marked a slot */
+ if ((slot_mask[tbf->trx] & (1 << ts)))
+ break;
+ }
+ }
+ /* mark first slot found, if none is marked already */
+ if (ts == 8 && first_ts >= 0) {
+ LOGP(DRLCMAC, LOGL_DEBUG, "- %s TBF=%d uses "
+ "TRX=%d TS=%d, so we mark\n",
+ (tbf->direction == GPRS_RLCMAC_UL_TBF)
+ ? "UL" : "DL",
+ tbf->tfi, tbf->trx, first_ts);
+ slot_mask[tbf->trx] |= (1 << first_ts);
+ } else
+ LOGP(DRLCMAC, LOGL_DEBUG, "- %s TBF=%d uses "
+ "already marked TRX=%d TS=%d\n",
+ (tbf->direction == GPRS_RLCMAC_UL_TBF)
+ ? "UL" : "DL",
+ tbf->tfi, tbf->trx, ts);
+ }
+ }
+
+ /* Now we have a list of marked slots. Every TBF uses at least one
+ * of these slots. */
+
+ /* schedule paging to all marked slots */
+ for (trx = 0; trx < 8; trx++) {
+ if (slot_mask[trx] == 0)
+ continue;
+ any_tbf = 1;
+ for (ts = 0; ts < 8; ts++) {
+ if ((slot_mask[trx] & (1 << ts))) {
+ /* schedule */
+ pag = talloc_zero(tall_pcu_ctx,
+ struct gprs_rlcmac_paging);
+ if (!pag)
+ return -ENOMEM;
+ pag->chan_needed = chan_needed;
+ memcpy(pag->identity_lv, identity_lv,
+ identity_lv[0] + 1);
+ llist_add(&pag->list,
+ &bts->trx[trx].pdch[ts].paging_list);
+ LOGP(DRLCMAC, LOGL_INFO, "Paging on PACCH of "
+ "TRX=%d TS=%d\n", trx, ts);
+ }
+ }
+ }
+
+ if (!any_tbf)
+ LOGP(DRLCMAC, LOGL_INFO, "No paging, because no TBF\n");
+
+ return 0;
+}
+
+struct gprs_rlcmac_paging *gprs_rlcmac_dequeue_paging(
+ struct gprs_rlcmac_pdch *pdch)
+{
+ struct gprs_rlcmac_paging *pag;
+
+ if (llist_empty(&pdch->paging_list))
+ return NULL;
+ pag = llist_entry(pdch->paging_list.next,
+ struct gprs_rlcmac_paging, list);
+ llist_del(&pag->list);
+
+ return pag;
+}
+
+struct msgb *gprs_rlcmac_send_packet_paging_request(
+ struct gprs_rlcmac_pdch *pdch)
+{
+ struct gprs_rlcmac_paging *pag;
+ struct msgb *msg;
+ unsigned wp = 0, len;
+
+ /* no paging, no message */
+ pag = gprs_rlcmac_dequeue_paging(pdch);
+ if (!pag)
+ return NULL;
+
+ LOGP(DRLCMAC, LOGL_DEBUG, "Scheduling paging\n");
+
+ /* alloc message */
+ msg = msgb_alloc(23, "pag ctrl block");
+ if (!msg)
+ return NULL;
+ bitvec *pag_vec = bitvec_alloc(23);
+ if (!pag_vec) {
+ msgb_free(msg);
+ return NULL;
+ }
+ wp = write_packet_paging_request(pag_vec);
+
+ /* loop until message is full */
+ while (pag) {
+ /* try to add paging */
+ if ((pag->identity_lv[1] & 0x07) == 4) {
+ /* TMSI */
+ LOGP(DRLCMAC, LOGL_DEBUG, "- TMSI=0x%08x\n",
+ ntohl(*((uint32_t *)(pag->identity_lv + 1))));
+ len = 1 + 1 + 1 + 32 + 2 + 1;
+ if (pag->identity_lv[0] != 5) {
+ LOGP(DRLCMAC, LOGL_ERROR, "TMSI paging with "
+ "MI != 5 octets!\n");
+ break;
+ }
+ } else {
+ /* MI */
+ LOGP(DRLCMAC, LOGL_DEBUG, "- MI=%s\n",
+ osmo_hexdump(pag->identity_lv + 1,
+ pag->identity_lv[0]));
+ len = 1 + 1 + 1 + 4 + (pag->identity_lv[0]<<3) + 2 + 1;
+ if (pag->identity_lv[0] > 8) {
+ LOGP(DRLCMAC, LOGL_ERROR, "Paging with "
+ "MI > 8 octets!\n");
+ break;
+ }
+ }
+ if (wp + len > 184) {
+ LOGP(DRLCMAC, LOGL_DEBUG, "- Does not fit, so schedule "
+ "next time\n");
+ /* put back paging record, because does not fit */
+ llist_add_tail(&pag->list, &pdch->paging_list);
+ break;
+ }
+ write_repeated_page_info(pag_vec, wp, pag->identity_lv[0],
+ pag->identity_lv + 1, pag->chan_needed);
+
+ pag = gprs_rlcmac_dequeue_paging(pdch);
+ }
+
+ bitvec_pack(pag_vec, msgb_put(msg, 23));
+ RlcMacDownlink_t * mac_control_block = (RlcMacDownlink_t *)talloc_zero(tall_pcu_ctx, RlcMacDownlink_t);
+ LOGP(DRLCMAC, LOGL_DEBUG, "+++++++++++++++++++++++++ TX : Packet Paging Request +++++++++++++++++++++++++\n");
+ decode_gsm_rlcmac_downlink(pag_vec, mac_control_block);
+ LOGPC(DCSN1, LOGL_NOTICE, "\n");
+ LOGP(DRLCMAC, LOGL_DEBUG, "------------------------- TX : Packet Paging Request -------------------------\n");
+ bitvec_free(pag_vec);
+ talloc_free(mac_control_block);
+
+ return msg;
+}
+
// GSM 04.08 9.1.18 Immediate assignment
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)
+ uint8_t polling, uint32_t fn, uint8_t single_block, uint8_t alpha,
+ uint8_t gamma)
{
unsigned wp = 0;
uint8_t plen;
@@ -344,9 +1197,9 @@ int write_immediate_assignment(bitvec * dest, uint8_t downlink, uint8_t ra,
//10.5.2.30 Request Reference
bitvec_write_field(dest, wp,ra,8); // RA
- bitvec_write_field(dest, wp,(fn / (26 * 51)) % 32,5); // T1'
- bitvec_write_field(dest, wp,fn % 51,6); // T3
- bitvec_write_field(dest, wp,fn % 26,5); // T2
+ bitvec_write_field(dest, wp,(ref_fn / (26 * 51)) % 32,5); // T1'
+ bitvec_write_field(dest, wp,ref_fn % 51,6); // T3
+ bitvec_write_field(dest, wp,ref_fn % 26,5); // T2
// 10.5.2.40 Timing Advance
bitvec_write_field(dest, wp,0x0,2); // spare
@@ -372,17 +1225,22 @@ int write_immediate_assignment(bitvec * dest, uint8_t downlink, uint8_t ra,
bitvec_write_field(dest, wp,0x1,1); // switch TFI : on
bitvec_write_field(dest, wp,tfi,5); // TFI
bitvec_write_field(dest, wp,0x0,1); // RLC acknowledged mode
- bitvec_write_field(dest, wp,0x0,1); // ALPHA = not present
- bitvec_write_field(dest, wp,0x0,5); // GAMMA power control parameter
+ if (alpha) {
+ bitvec_write_field(dest, wp,0x1,1); // ALPHA = present
+ bitvec_write_field(dest, wp,alpha,4); // ALPHA
+ } else {
+ bitvec_write_field(dest, wp,0x0,1); // ALPHA = not present
+ }
+ bitvec_write_field(dest, wp,gamma,5); // GAMMA power control parameter
bitvec_write_field(dest, wp,polling,1); // Polling Bit
bitvec_write_field(dest, wp,!polling,1); // TA_VALID ???
bitvec_write_field(dest, wp,0x1,1); // switch TIMING_ADVANCE_INDEX = on
bitvec_write_field(dest, wp,0x0,4); // TIMING_ADVANCE_INDEX
if (polling) {
bitvec_write_field(dest, wp,0x1,1); // TBF Starting TIME present
- bitvec_write_field(dest, wp,(poll_fn / (26 * 51)) % 32,5); // T1'
- bitvec_write_field(dest, wp,poll_fn % 51,6); // T3
- bitvec_write_field(dest, wp,poll_fn % 26,5); // T2
+ bitvec_write_field(dest, wp,(fn / (26 * 51)) % 32,5); // T1'
+ bitvec_write_field(dest, wp,fn % 51,6); // T3
+ bitvec_write_field(dest, wp,fn % 26,5); // T2
} else {
bitvec_write_field(dest, wp,0x0,1); // TBF Starting TIME present
}
@@ -396,20 +1254,38 @@ int write_immediate_assignment(bitvec * dest, uint8_t downlink, uint8_t ra,
// GMS 04.08 10.5.2.37b 10.5.2.16
bitvec_write_field(dest, wp, 3, 2); // "HH"
bitvec_write_field(dest, wp, 0, 2); // "0" Packet Uplink Assignment
- bitvec_write_field(dest, wp, 1, 1); // Block Allocation : Not Single Block Allocation
- bitvec_write_field(dest, wp, tfi, 5); // TFI_ASSIGNMENT Temporary Flow Identity
- bitvec_write_field(dest, wp, 0, 1); // POLLING
- bitvec_write_field(dest, wp, 0, 1); // ALLOCATION_TYPE: dynamic
- bitvec_write_field(dest, wp, usf, 3); // USF
- bitvec_write_field(dest, wp, 0, 1); // USF_GRANULARITY
- bitvec_write_field(dest, wp, 0 , 1); // "0" power control: Not Present
- bitvec_write_field(dest, wp, bts->initial_cs-1, 2); // CHANNEL_CODING_COMMAND
- bitvec_write_field(dest, wp, 1, 1); // TLLI_BLOCK_CHANNEL_CODING
- bitvec_write_field(dest, wp, 1 , 1); // "1" Alpha : Present
- bitvec_write_field(dest, wp, 0, 4); // Alpha
- bitvec_write_field(dest, wp, 0, 5); // Gamma
- bitvec_write_field(dest, wp, 0, 1); // TIMING_ADVANCE_INDEX_FLAG
- bitvec_write_field(dest, wp, 0, 1); // TBF_STARTING_TIME_FLAG
+ if (single_block) {
+ bitvec_write_field(dest, wp, 0, 1); // Block Allocation : Single Block Allocation
+ if (alpha) {
+ bitvec_write_field(dest, wp,0x1,1); // ALPHA = present
+ bitvec_write_field(dest, wp,alpha,4); // ALPHA = present
+ } else
+ bitvec_write_field(dest, wp,0x0,1); // ALPHA = not present
+ bitvec_write_field(dest, wp,gamma,5); // GAMMA power control parameter
+ bitvec_write_field(dest, wp, 0, 1); // TIMING_ADVANCE_INDEX_FLAG
+ bitvec_write_field(dest, wp, 1, 1); // TBF_STARTING_TIME_FLAG
+ bitvec_write_field(dest, wp,(fn / (26 * 51)) % 32,5); // T1'
+ bitvec_write_field(dest, wp,fn % 51,6); // T3
+ bitvec_write_field(dest, wp,fn % 26,5); // T2
+ } else {
+ bitvec_write_field(dest, wp, 1, 1); // Block Allocation : Not Single Block Allocation
+ bitvec_write_field(dest, wp, tfi, 5); // TFI_ASSIGNMENT Temporary Flow Identity
+ bitvec_write_field(dest, wp, 0, 1); // POLLING
+ bitvec_write_field(dest, wp, 0, 1); // ALLOCATION_TYPE: dynamic
+ bitvec_write_field(dest, wp, usf, 3); // USF
+ bitvec_write_field(dest, wp, 0, 1); // USF_GRANULARITY
+ bitvec_write_field(dest, wp, 0, 1); // "0" power control: Not Present
+ bitvec_write_field(dest, wp, bts->initial_cs_ul-1, 2); // CHANNEL_CODING_COMMAND
+ bitvec_write_field(dest, wp, 1, 1); // TLLI_BLOCK_CHANNEL_CODING
+ if (alpha) {
+ bitvec_write_field(dest, wp,0x1,1); // ALPHA = present
+ bitvec_write_field(dest, wp,alpha,4); // ALPHA
+ } else
+ bitvec_write_field(dest, wp,0x0,1); // ALPHA = not present
+ bitvec_write_field(dest, wp,gamma,5); // GAMMA power control parameter
+ bitvec_write_field(dest, wp, 0, 1); // TIMING_ADVANCE_INDEX_FLAG
+ bitvec_write_field(dest, wp, 0, 1); // TBF_STARTING_TIME_FLAG
+ }
}
return plen;
@@ -417,14 +1293,14 @@ int write_immediate_assignment(bitvec * dest, uint8_t downlink, uint8_t ra,
/* generate uplink assignment */
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)
{
// TODO We should use our implementation of encode RLC/MAC Control messages.
struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts;
unsigned wp = 0;
- int i;
+ uint8_t ts;
bitvec_write_field(dest, wp,0x1,2); // Payload Type
bitvec_write_field(dest, wp,0x0,2); // Uplink block with TDMA framenumber (N+13)
@@ -445,18 +1321,18 @@ void write_packet_uplink_assignment(bitvec * dest, uint8_t old_tfi,
}
bitvec_write_field(dest, wp,0x0,1); // Message escape
- bitvec_write_field(dest, wp, bts->initial_cs-1, 2); // CHANNEL_CODING_COMMAND
+ bitvec_write_field(dest, wp,bts->initial_cs_ul-1, 2); // CHANNEL_CODING_COMMAND
bitvec_write_field(dest, wp,0x1,1); // TLLI_BLOCK_CHANNEL_CODING
bitvec_write_field(dest, wp,0x1,1); // switch TIMING_ADVANCE_VALUE = on
- bitvec_write_field(dest, wp,ta,6); // TIMING_ADVANCE_VALUE
+ bitvec_write_field(dest, wp,tbf->ta,6); // TIMING_ADVANCE_VALUE
bitvec_write_field(dest, wp,0x0,1); // switch TIMING_ADVANCE_INDEX = off
#if 1
bitvec_write_field(dest, wp,0x1,1); // Frequency Parameters information elements = present
- bitvec_write_field(dest, wp,tsc,3); // Training Sequence Code (TSC)
+ bitvec_write_field(dest, wp,tbf->tsc,3); // Training Sequence Code (TSC)
bitvec_write_field(dest, wp,0x0,2); // ARFCN = present
- bitvec_write_field(dest, wp,arfcn,10); // ARFCN
+ bitvec_write_field(dest, wp,tbf->arfcn,10); // ARFCN
#else
bitvec_write_field(dest, wp,0x0,1); // Frequency Parameters = off
#endif
@@ -468,16 +1344,22 @@ void write_packet_uplink_assignment(bitvec * dest, uint8_t old_tfi,
bitvec_write_field(dest, wp,0x0,1); // USF_GRANULARITY
bitvec_write_field(dest, wp,0x1,1); // switch TFI : on
- bitvec_write_field(dest, wp,new_tfi,5);// TFI
+ bitvec_write_field(dest, wp,tbf->tfi,5);// TFI
bitvec_write_field(dest, wp,0x0,1); //
bitvec_write_field(dest, wp,0x0,1); // TBF Starting Time = off
- bitvec_write_field(dest, wp,0x0,1); // Timeslot Allocation
+ if (alpha || gamma) {
+ bitvec_write_field(dest, wp,0x1,1); // Timeslot Allocation with Power Control
+ bitvec_write_field(dest, wp,alpha,4); // ALPHA
+ } else
+ bitvec_write_field(dest, wp,0x0,1); // Timeslot Allocation
- for (i = 0; i < 8; i++) {
- if (tn == i) {
+ for (ts = 0; ts < 8; ts++) {
+ if (tbf->pdch[ts]) {
bitvec_write_field(dest, wp,0x1,1); // USF_TN(i): on
- bitvec_write_field(dest, wp,usf,3); // USF_TN(i)
+ bitvec_write_field(dest, wp,tbf->dir.ul.usf[ts],3); // USF_TN(i)
+ if (alpha || gamma)
+ bitvec_write_field(dest, wp,gamma,5); // GAMMA power control parameter
} else
bitvec_write_field(dest, wp,0x0,1); // USF_TN(i): off
}
@@ -487,12 +1369,12 @@ void write_packet_uplink_assignment(bitvec * dest, uint8_t old_tfi,
/* generate downlink assignment */
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)
{
// Packet downlink assignment TS 44.060 11.2.7
- int i;
+ uint8_t tn;
block->PAYLOAD_TYPE = 0x1; // RLC/MAC control block that does not include the optional octets of the RLC/MAC control header
block->RRBP = 0x0; // N+13
@@ -511,35 +1393,39 @@ void write_packet_downlink_assignment(RlcMacDownlink_t * block, uint8_t old_tfi,
block->u.Packet_Downlink_Assignment.MAC_MODE = 0x0; // Dynamic Allocation
block->u.Packet_Downlink_Assignment.RLC_MODE = 0x0; // RLC acknowledged mode
block->u.Packet_Downlink_Assignment.CONTROL_ACK = old_downlink; // NW establishes no new DL TBF for the MS with running timer T3192
- block->u.Packet_Downlink_Assignment.TIMESLOT_ALLOCATION = 0x80 >> tn; // timeslot(s)
+ block->u.Packet_Downlink_Assignment.TIMESLOT_ALLOCATION = 0; // timeslot(s)
+ for (tn = 0; tn < 8; tn++) {
+ if (tbf->pdch[tn])
+ block->u.Packet_Downlink_Assignment.TIMESLOT_ALLOCATION |= 0x80 >> tn; // timeslot(s)
+ }
block->u.Packet_Downlink_Assignment.Packet_Timing_Advance.Exist_TIMING_ADVANCE_VALUE = 0x1; // TIMING_ADVANCE_VALUE = on
- block->u.Packet_Downlink_Assignment.Packet_Timing_Advance.TIMING_ADVANCE_VALUE = ta; // TIMING_ADVANCE_VALUE
+ block->u.Packet_Downlink_Assignment.Packet_Timing_Advance.TIMING_ADVANCE_VALUE = tbf->ta; // TIMING_ADVANCE_VALUE
block->u.Packet_Downlink_Assignment.Packet_Timing_Advance.Exist_IndexAndtimeSlot = 0x0; // TIMING_ADVANCE_INDEX = off
block->u.Packet_Downlink_Assignment.Exist_P0_and_BTS_PWR_CTRL_MODE = 0x0; // POWER CONTROL = off
block->u.Packet_Downlink_Assignment.Exist_Frequency_Parameters = 0x1; // Frequency Parameters = on
- block->u.Packet_Downlink_Assignment.Frequency_Parameters.TSC = tsc; // Training Sequence Code (TSC)
+ block->u.Packet_Downlink_Assignment.Frequency_Parameters.TSC = tbf->tsc; // Training Sequence Code (TSC)
block->u.Packet_Downlink_Assignment.Frequency_Parameters.UnionType = 0x0; // ARFCN = on
- block->u.Packet_Downlink_Assignment.Frequency_Parameters.u.ARFCN = arfcn; // ARFCN
+ block->u.Packet_Downlink_Assignment.Frequency_Parameters.u.ARFCN = tbf->arfcn; // ARFCN
block->u.Packet_Downlink_Assignment.Exist_DOWNLINK_TFI_ASSIGNMENT = 0x1; // DOWNLINK TFI ASSIGNMENT = on
- block->u.Packet_Downlink_Assignment.DOWNLINK_TFI_ASSIGNMENT = new_tfi; // TFI
+ block->u.Packet_Downlink_Assignment.DOWNLINK_TFI_ASSIGNMENT = tbf->tfi; // TFI
block->u.Packet_Downlink_Assignment.Exist_Power_Control_Parameters = 0x1; // Power Control Parameters = on
- block->u.Packet_Downlink_Assignment.Power_Control_Parameters.ALPHA = 0x0; // ALPHA
+ block->u.Packet_Downlink_Assignment.Power_Control_Parameters.ALPHA = alpha; // ALPHA
- for (i = 0; i < 8; i++)
+ for (tn = 0; tn < 8; tn++)
{
- if (tn == i)
+ if (tbf->pdch[tn])
{
- block->u.Packet_Downlink_Assignment.Power_Control_Parameters.Slot[i].Exist = 0x1; // Slot[i] = on
- block->u.Packet_Downlink_Assignment.Power_Control_Parameters.Slot[i].GAMMA_TN = 0x0; // GAMMA_TN
+ block->u.Packet_Downlink_Assignment.Power_Control_Parameters.Slot[tn].Exist = 0x1; // Slot[i] = on
+ block->u.Packet_Downlink_Assignment.Power_Control_Parameters.Slot[tn].GAMMA_TN = gamma; // GAMMA_TN
}
else
{
- block->u.Packet_Downlink_Assignment.Power_Control_Parameters.Slot[i].Exist = 0x0; // Slot[i] = off
+ block->u.Packet_Downlink_Assignment.Power_Control_Parameters.Slot[tn].Exist = 0x0; // Slot[i] = off
}
}
@@ -556,6 +1442,7 @@ void write_packet_uplink_ack(RlcMacDownlink_t * block, struct gprs_rlcmac_tbf *t
char show_v_n[65];
+ struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts;
uint8_t rbb = 0;
uint16_t i, bbn;
uint16_t mod_sns_half = (tbf->sns >> 1) - 1;
@@ -574,7 +1461,7 @@ void write_packet_uplink_ack(RlcMacDownlink_t * block, struct gprs_rlcmac_tbf *t
block->u.Packet_Uplink_Ack_Nack.UPLINK_TFI = tbf->tfi; // Uplink TFI
block->u.Packet_Uplink_Ack_Nack.UnionType = 0x0; // PU_AckNack_GPRS = on
- block->u.Packet_Uplink_Ack_Nack.u.PU_AckNack_GPRS_Struct.CHANNEL_CODING_COMMAND = 0x0; // CS1
+ block->u.Packet_Uplink_Ack_Nack.u.PU_AckNack_GPRS_Struct.CHANNEL_CODING_COMMAND = bts->initial_cs_ul - 1; // CS1
block->u.Packet_Uplink_Ack_Nack.u.PU_AckNack_GPRS_Struct.Ack_Nack_Description.FINAL_ACK_INDICATION = final; // FINAL ACK INDICATION
block->u.Packet_Uplink_Ack_Nack.u.PU_AckNack_GPRS_Struct.Ack_Nack_Description.STARTING_SEQUENCE_NUMBER = tbf->dir.ul.v_r; // STARTING_SEQUENCE_NUMBER
// RECEIVE_BLOCK_BITMAP
@@ -609,22 +1496,68 @@ void write_packet_uplink_ack(RlcMacDownlink_t * block, struct gprs_rlcmac_tbf *t
block->u.Packet_Uplink_Ack_Nack.u.PU_AckNack_GPRS_Struct.Common_Uplink_Ack_Nack_Data.Exist_Power_Control_Parameters = 0x0;
}
+unsigned write_packet_paging_request(bitvec * dest)
+{
+ unsigned wp = 0;
+
+ bitvec_write_field(dest, wp,0x1,2); // Payload Type
+ bitvec_write_field(dest, wp,0x0,3); // No polling
+ bitvec_write_field(dest, wp,0x0,3); // Uplink state flag
+ bitvec_write_field(dest, wp,0x22,6); // MESSAGE TYPE
+
+ bitvec_write_field(dest, wp,0x0,2); // Page Mode
+
+ bitvec_write_field(dest, wp,0x0,1); // No PERSISTENCE_LEVEL
+ bitvec_write_field(dest, wp,0x0,1); // No NLN
+
+ return wp;
+}
+
+unsigned write_repeated_page_info(bitvec * dest, unsigned& wp, uint8_t len,
+ uint8_t *identity, uint8_t chan_needed)
+{
+ bitvec_write_field(dest, wp,0x1,1); // Repeated Page info exists
+
+ bitvec_write_field(dest, wp,0x1,1); // RR connection paging
+
+ if ((identity[0] & 0x07) == 4) {
+ bitvec_write_field(dest, wp,0x0,1); // TMSI
+ identity++;
+ len--;
+ } else {
+ bitvec_write_field(dest, wp,0x0,1); // MI
+ bitvec_write_field(dest, wp,len,4); // MI len
+ }
+ while (len) {
+ bitvec_write_field(dest, wp,*identity++,8); // MI data
+ len--;
+ }
+ bitvec_write_field(dest, wp,chan_needed,2); // CHANNEL_NEEDED
+ bitvec_write_field(dest, wp,0x0,1); // No eMLPP_PRIORITY
+
+ return wp;
+}
+
/* Send Uplink unit-data to SGSN. */
int gprs_rlcmac_tx_ul_ud(gprs_rlcmac_tbf *tbf)
{
- const uint8_t qos_profile = QOS_PROFILE;
+ uint8_t qos_profile[3];
struct msgb *llc_pdu;
unsigned msg_len = NS_HDR_LEN + BSSGP_HDR_LEN + tbf->llc_index;
- LOGP(DBSSGP, LOGL_INFO, "LLC [PCU -> SGSN] TFI: %u TLLI: 0x%08x %s\n", tbf->tfi, tbf->tlli, osmo_hexdump(tbf->llc_frame, tbf->llc_index));
+ LOGP(DBSSGP, LOGL_INFO, "LLC [PCU -> SGSN] TFI: %u TLLI: 0x%08x len=%d\n", tbf->tfi, tbf->tlli, tbf->llc_index);
if (!bctx) {
LOGP(DBSSGP, LOGL_ERROR, "No bctx\n");
return -EIO;
}
llc_pdu = msgb_alloc_headroom(msg_len, msg_len,"llc_pdu");
- msgb_tvlv_push(llc_pdu, BSSGP_IE_LLC_PDU, sizeof(uint8_t)*tbf->llc_index, tbf->llc_frame);
- bssgp_tx_ul_ud(bctx, tbf->tlli, &qos_profile, llc_pdu);
+ uint8_t *buf = msgb_push(llc_pdu, TL16V_GROSS_LEN(sizeof(uint8_t)*tbf->llc_index));
+ tl16v_put(buf, BSSGP_IE_LLC_PDU, sizeof(uint8_t)*tbf->llc_index, tbf->llc_frame);
+ qos_profile[0] = QOS_PROFILE >> 16;
+ qos_profile[1] = QOS_PROFILE >> 8;
+ qos_profile[2] = QOS_PROFILE;
+ bssgp_tx_ul_ud(bctx, tbf->tlli, qos_profile, llc_pdu);
return 0;
}
diff --git a/src/gprs_rlcmac.h b/src/gprs_rlcmac.h
index dc4c648b..1900d89b 100644
--- a/src/gprs_rlcmac.h
+++ b/src/gprs_rlcmac.h
@@ -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_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 */
+
+/*
+ * paging entry
+ */
+struct gprs_rlcmac_paging {
+ struct llist_head list;
+ uint8_t chan_needed;
+ uint8_t identity_lv[9];
+};
+
+/*
+ * single block allocation entry
+ */
+struct gprs_rlcmac_sba {
+ struct llist_head list;
+ uint8_t trx;
+ uint8_t ts;
+ uint32_t fn;
+ uint8_t ta;
+};
+
+/*
+ * coding scheme info
+ */
+struct gprs_rlcmac_cs {
+ uint8_t block_length;
+ uint8_t block_data;
+ uint8_t block_payload;
};
-extern struct llist_head gprs_rlcmac_tbfs;
+extern struct gprs_rlcmac_cs gprs_rlcmac_cs[];
+
+int sba_alloc(uint8_t *_trx, uint8_t *_ts, uint32_t *_fn, uint8_t ta);
-int tfi_alloc(uint8_t *_trx, uint8_t *_ts);
+struct gprs_rlcmac_sba *sba_find(uint8_t trx, uint8_t ts, uint32_t fn);
-struct gprs_rlcmac_tbf *tbf_alloc(uint8_t tfi, uint8_t trx, uint8_t ts);
+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_by_tfi(uint8_t tfi, int direction);
+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_tlli(uint32_t tlli, int direction);
+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_poll_fn(uint32_t fn);
+struct gprs_rlcmac_tbf *tbf_by_tlli(uint32_t tlli,
+ enum gprs_rlcmac_tbf_direction dir);
-int find_free_usf(uint8_t trx, uint8_t ts);
+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
diff --git a/src/gprs_rlcmac_data.cpp b/src/gprs_rlcmac_data.cpp
index 12ac1ae6..edc324b0 100644
--- a/src/gprs_rlcmac_data.cpp
+++ b/src/gprs_rlcmac_data.cpp
@@ -22,11 +22,22 @@
#include <gprs_rlcmac.h>
#include <pcu_l1_if.h>
-/* After receiving these framess, we send ack/nack. */
-#define ACK_AFTER_FRAMES 20
+extern void *tall_pcu_ctx;
-/* If acknowledgement to uplink/downlin assignmentshould be polled */
-#define POLLING_ASSIGNMENT 0
+extern "C" {
+int bssgp_tx_llc_discarded(struct bssgp_bvc_ctx *bctx, uint32_t tlli,
+ uint8_t num_frames, uint32_t num_octets);
+}
+
+/* After receiving these frames, we send ack/nack. */
+#define SEND_ACK_AFTER_FRAMES 20
+
+/* After sending these frames, we poll for ack/nack. */
+#define POLL_ACK_AFTER_FRAMES 20
+
+/* If acknowledgement to uplink/downlink assignmentshould be polled */
+#define POLLING_ASSIGNMENT_DL 1
+#define POLLING_ASSIGNMENT_UL 1
extern "C" {
/* TS 04.60 10.2.2 */
@@ -62,22 +73,49 @@ struct rlc_li_field {
} __attribute__ ((packed));
}
+static void gprs_rlcmac_downlink_assignment(gprs_rlcmac_tbf *tbf, uint8_t poll,
+ char *imsi);
+
+static int gprs_rlcmac_diag(struct gprs_rlcmac_tbf *tbf)
+{
+ if ((tbf->state_flags & (1 << GPRS_RLCMAC_FLAG_CCCH)))
+ LOGP(DRLCMAC, LOGL_NOTICE, "- Assignment was on CCCH\n");
+ if ((tbf->state_flags & (1 << GPRS_RLCMAC_FLAG_PACCH)))
+ LOGP(DRLCMAC, LOGL_NOTICE, "- Assignment was on PACCH\n");
+ if ((tbf->state_flags & (1 << GPRS_RLCMAC_FLAG_UL_DATA)))
+ LOGP(DRLCMAC, LOGL_NOTICE, "- Uplink data was received\n");
+ else if (tbf->direction == GPRS_RLCMAC_UL_TBF)
+ LOGP(DRLCMAC, LOGL_NOTICE, "- No uplink data received yet\n");
+ if ((tbf->state_flags & (1 << GPRS_RLCMAC_FLAG_DL_ACK)))
+ LOGP(DRLCMAC, LOGL_NOTICE, "- Downlink ACK was received\n");
+ else if (tbf->direction == GPRS_RLCMAC_DL_TBF)
+ LOGP(DRLCMAC, LOGL_NOTICE, "- No downlink ACK received yet\n");
+
+ return 0;
+}
+
int gprs_rlcmac_poll_timeout(struct gprs_rlcmac_tbf *tbf)
{
- LOGP(DRLCMAC, LOGL_NOTICE, "Poll timeout for TBF=%d\n", tbf->tfi);
+ LOGP(DRLCMAC, LOGL_NOTICE, "Poll timeout for %s TBF=%d\n",
+ (tbf->direction == GPRS_RLCMAC_UL_TBF) ? "UL" : "DL", tbf->tfi);
tbf->poll_state = GPRS_RLCMAC_POLL_NONE;
if (tbf->ul_ack_state == GPRS_RLCMAC_UL_ACK_WAIT_ACK) {
- LOGP(DRLCMAC, LOGL_DEBUG, "- Timeout for polling PACKET "
- "CONTROL ACK for PACKET UPLINK ACK\n");
+ if (!(tbf->state_flags & (1 << GPRS_RLCMAC_FLAG_TO_UL_ACK))) {
+ LOGP(DRLCMAC, LOGL_NOTICE, "- Timeout for polling "
+ "PACKET CONTROL ACK for PACKET UPLINK ACK\n");
+ gprs_rlcmac_diag(tbf);
+ tbf->state_flags |= (1 << GPRS_RLCMAC_FLAG_TO_UL_ACK);
+ }
tbf->ul_ack_state = GPRS_RLCMAC_UL_ACK_NONE;
if (tbf->state == GPRS_RLCMAC_FINISHED) {
struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts;
tbf->dir.ul.n3103++;
if (tbf->dir.ul.n3103 == bts->n3103) {
- LOGP(DRLCMAC, LOGL_DEBUG, "- N3103 exceeded\n");
+ LOGP(DRLCMAC, LOGL_NOTICE,
+ "- N3103 exceeded\n");
tbf_new_state(tbf, GPRS_RLCMAC_RELEASING);
tbf_timer_start(tbf, 3169, bts->t3169, 0);
return 0;
@@ -87,46 +125,140 @@ int gprs_rlcmac_poll_timeout(struct gprs_rlcmac_tbf *tbf)
}
} else
if (tbf->ul_ass_state == GPRS_RLCMAC_UL_ASS_WAIT_ACK) {
- LOGP(DRLCMAC, LOGL_DEBUG, "- Timeout for polling PACKET "
- "CONTROL ACK for PACKET UPLINK ASSIGNMENT.\n");
+ struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts;
+
+ if (!(tbf->state_flags & (1 << GPRS_RLCMAC_FLAG_TO_UL_ASS))) {
+ LOGP(DRLCMAC, LOGL_NOTICE, "- Timeout for polling "
+ "PACKET CONTROL ACK for PACKET UPLINK "
+ "ASSIGNMENT.\n");
+ gprs_rlcmac_diag(tbf);
+ tbf->state_flags |= (1 << GPRS_RLCMAC_FLAG_TO_UL_ASS);
+ }
tbf->ul_ass_state = GPRS_RLCMAC_UL_ASS_NONE;
+ tbf->n3105++;
+ if (tbf->n3105 == bts->n3105) {
+ LOGP(DRLCMAC, LOGL_NOTICE, "- N3105 exceeded\n");
+ tbf_new_state(tbf, GPRS_RLCMAC_RELEASING);
+ tbf_timer_start(tbf, 3195, bts->t3195, 0);
+ return 0;
+ }
+ /* reschedule UL assignment */
+ tbf->ul_ass_state = GPRS_RLCMAC_UL_ASS_SEND_ASS;
} else
if (tbf->dl_ass_state == GPRS_RLCMAC_DL_ASS_WAIT_ACK) {
- LOGP(DRLCMAC, LOGL_DEBUG, "- Timeout for polling PACKET "
- "CONTROL ACK for PACKET DOWNLINK ASSIGNMENT.\n");
+ struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts;
+
+ if (!(tbf->state_flags & (1 << GPRS_RLCMAC_FLAG_TO_DL_ASS))) {
+ LOGP(DRLCMAC, LOGL_NOTICE, "- Timeout for polling "
+ "PACKET CONTROL ACK for PACKET DOWNLINK "
+ "ASSIGNMENT.\n");
+ gprs_rlcmac_diag(tbf);
+ tbf->state_flags |= (1 << GPRS_RLCMAC_FLAG_TO_DL_ASS);
+ }
tbf->dl_ass_state = GPRS_RLCMAC_DL_ASS_NONE;
- /* in case out downlink assigment failed: */
- if (tbf->state == GPRS_RLCMAC_ASSIGN) {
- LOGP(DRLCMAC, LOGL_DEBUG, "- Assignment failed\n");
- tbf_free(tbf);
+ tbf->n3105++;
+ if (tbf->n3105 == bts->n3105) {
+ LOGP(DRLCMAC, LOGL_NOTICE, "- N3105 exceeded\n");
+ tbf_new_state(tbf, GPRS_RLCMAC_RELEASING);
+ tbf_timer_start(tbf, 3195, bts->t3195, 0);
+ return 0;
}
+ /* reschedule DL assignment */
+ tbf->dl_ass_state = GPRS_RLCMAC_DL_ASS_SEND_ASS;
} else
- if (tbf->direction == GPRS_RLCMAC_DL_TBF)
- {
+ if (tbf->direction == GPRS_RLCMAC_DL_TBF) {
struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts;
- LOGP(DRLCMAC, LOGL_DEBUG, "- Timeout for polling PACKET "
- " DOWNLINK ACK.\n");
- tbf->dir.dl.n3105++;
- if (tbf->dir.dl.n3105 == bts->n3105) {
- LOGP(DRLCMAC, LOGL_DEBUG, "- N3105 exceeded\n");
+ if (!(tbf->state_flags & (1 << GPRS_RLCMAC_FLAG_TO_DL_ACK))) {
+ LOGP(DRLCMAC, LOGL_NOTICE, "- Timeout for polling "
+ "PACKET DOWNLINK ACK.\n");
+ gprs_rlcmac_diag(tbf);
+ tbf->state_flags |= (1 << GPRS_RLCMAC_FLAG_TO_DL_ACK);
+ }
+ tbf->n3105++;
+ if (tbf->n3105 == bts->n3105) {
+ LOGP(DRLCMAC, LOGL_NOTICE, "- N3105 exceeded\n");
tbf_new_state(tbf, GPRS_RLCMAC_RELEASING);
tbf_timer_start(tbf, 3195, bts->t3195, 0);
return 0;
}
+ /* resend IMM.ASS on CCCH on timeout */
+ if ((tbf->state_flags & (1 << GPRS_RLCMAC_FLAG_CCCH))
+ && !(tbf->state_flags & (1 << GPRS_RLCMAC_FLAG_DL_ACK))) {
+ LOGP(DRLCMAC, LOGL_DEBUG, "Re-send dowlink assignment "
+ "for TBF=%d on PCH (IMSI=%s)\n", tbf->tfi,
+ tbf->dir.dl.imsi);
+ /* send immediate assignment */
+ gprs_rlcmac_downlink_assignment(tbf, 0, tbf->dir.dl.imsi);
+ tbf->dir.dl.wait_confirm = 1;
+ }
+ } else
+ LOGP(DRLCMAC, LOGL_ERROR, "- Poll Timeout, but no event!\n");
+
+ return 0;
+}
+
+static uint8_t get_ms_class_by_capability(MS_Radio_Access_capability_t *cap)
+{
+ int i;
+
+ for (i = 0; i < cap->Count_MS_RA_capability_value; i++) {
+ if (!cap->MS_RA_capability_value[i].u.Content.Exist_Multislot_capability)
+ continue;
+ if (!cap->MS_RA_capability_value[i].u.Content.Multislot_capability.Exist_GPRS_multislot_class)
+ continue;
+ return cap->MS_RA_capability_value[i].u.Content.Multislot_capability.GPRS_multislot_class;
}
return 0;
}
+static struct gprs_rlcmac_tbf *alloc_ul_tbf(int8_t use_trx, int8_t first_ts,
+ uint8_t ms_class, uint32_t tlli, uint8_t ta,
+ struct gprs_rlcmac_tbf *dl_tbf)
+{
+ struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts;
+ uint8_t trx, ts;
+ struct gprs_rlcmac_tbf *tbf;
+ uint8_t tfi;
+
+ /* create new TBF, use sme TRX as DL TBF */
+ tfi = tfi_alloc(GPRS_RLCMAC_UL_TBF, &trx, &ts, use_trx, first_ts);
+ if (tfi < 0) {
+ LOGP(DRLCMAC, LOGL_NOTICE, "No PDCH ressource\n");
+ /* FIXME: send reject */
+ return NULL;
+ }
+ /* use multislot class of downlink TBF */
+ tbf = tbf_alloc(dl_tbf, GPRS_RLCMAC_UL_TBF, tfi, trx, ts, ms_class, 0);
+ if (!tbf) {
+ LOGP(DRLCMAC, LOGL_NOTICE, "No PDCH ressource\n");
+ /* FIXME: send reject */
+ return NULL;
+ }
+ tbf->tlli = tlli;
+ tbf->tlli_valid = 1; /* no contention resolution */
+ tbf->dir.ul.contention_resolution_done = 1;
+ tbf->ta = ta; /* use current TA */
+ tbf_new_state(tbf, GPRS_RLCMAC_ASSIGN);
+ tbf->state_flags |= (1 << GPRS_RLCMAC_FLAG_PACCH);
+ tbf_timer_start(tbf, 3169, bts->t3169, 0);
+
+ return tbf;
+}
+
+
+
/* Received Uplink RLC control block. */
-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)
{
- uint8_t tfi = 0;
+ int8_t tfi = 0; /* must be signed */
uint32_t tlli = 0;
struct gprs_rlcmac_tbf *tbf;
+ int rc;
- RlcMacUplink_t * ul_control_block = (RlcMacUplink_t *)malloc(sizeof(RlcMacUplink_t));
+ RlcMacUplink_t * ul_control_block = (RlcMacUplink_t *)talloc_zero(tall_pcu_ctx, RlcMacUplink_t);
LOGP(DRLCMAC, LOGL_DEBUG, "+++++++++++++++++++++++++ RX : Uplink Control Block +++++++++++++++++++++++++\n");
decode_gsm_rlcmac_uplink(rlc_block, ul_control_block);
LOGPC(DCSN1, LOGL_NOTICE, "\n");
@@ -134,10 +266,11 @@ int gprs_rlcmac_rcv_control_block(bitvec *rlc_block, uint32_t fn)
switch (ul_control_block->u.MESSAGE_TYPE) {
case MT_PACKET_CONTROL_ACK:
tlli = ul_control_block->u.Packet_Control_Acknowledgement.TLLI;
- tbf = tbf_by_poll_fn(fn);
+ tbf = tbf_by_poll_fn(fn, trx, ts);
if (!tbf) {
LOGP(DRLCMAC, LOGL_NOTICE, "PACKET CONTROL ACK with "
- "unknown FN=%u TLL=0x%08x\n", fn, tlli);
+ "unknown FN=%u TLL=0x%08x (TRX %d TS %d)\n",
+ fn, tlli, trx, ts);
break;
}
tfi = tbf->tfi;
@@ -153,17 +286,64 @@ int gprs_rlcmac_rcv_control_block(bitvec *rlc_block, uint32_t fn)
if (tbf->ul_ack_state == GPRS_RLCMAC_UL_ACK_WAIT_ACK) {
LOGP(DRLCMAC, LOGL_DEBUG, "TBF: [UPLINK] END TFI: %u TLLI: 0x%08x \n", tbf->tfi, tbf->tlli);
tbf->ul_ack_state = GPRS_RLCMAC_UL_ACK_NONE;
+ if ((tbf->state_flags &
+ (1 << GPRS_RLCMAC_FLAG_TO_UL_ACK))) {
+ tbf->state_flags &=
+ ~(1 << GPRS_RLCMAC_FLAG_TO_UL_ACK);
+ LOGP(DRLCMAC, LOGL_NOTICE, "Recovered uplink "
+ "ack\n");
+ }
tbf_free(tbf);
break;
}
if (tbf->dl_ass_state == GPRS_RLCMAC_DL_ASS_WAIT_ACK) {
LOGP(DRLCMAC, LOGL_DEBUG, "TBF: [UPLINK] DOWNLINK ASSIGNED TFI: %u TLLI: 0x%08x \n", tbf->tfi, tbf->tlli);
+ /* reset N3105 */
+ tbf->n3105 = 0;
tbf->dl_ass_state = GPRS_RLCMAC_DL_ASS_NONE;
+ if (tbf->direction == GPRS_RLCMAC_UL_TBF)
+ tbf = tbf_by_tlli(tbf->tlli,
+ GPRS_RLCMAC_DL_TBF);
+ if (!tbf) {
+ LOGP(DRLCMAC, LOGL_ERROR, "Got ACK, but DL "
+ "TBF is gone\n");
+ break;
+ }
+ tbf_new_state(tbf, GPRS_RLCMAC_FLOW);
+ /* stop pending assignment timer */
+ tbf_timer_stop(tbf);
+ if ((tbf->state_flags &
+ (1 << GPRS_RLCMAC_FLAG_TO_DL_ASS))) {
+ tbf->state_flags &=
+ ~(1 << GPRS_RLCMAC_FLAG_TO_DL_ASS);
+ LOGP(DRLCMAC, LOGL_NOTICE, "Recovered downlink "
+ "assignment\n");
+ }
+ tbf_assign_control_ts(tbf);
break;
}
if (tbf->ul_ass_state == GPRS_RLCMAC_UL_ASS_WAIT_ACK) {
LOGP(DRLCMAC, LOGL_DEBUG, "TBF: [DOWNLINK] UPLINK ASSIGNED TFI: %u TLLI: 0x%08x \n", tbf->tfi, tbf->tlli);
+ /* reset N3105 */
+ tbf->n3105 = 0;
tbf->ul_ass_state = GPRS_RLCMAC_UL_ASS_NONE;
+ if (tbf->direction == GPRS_RLCMAC_DL_TBF)
+ tbf = tbf_by_tlli(tbf->tlli,
+ GPRS_RLCMAC_UL_TBF);
+ if (!tbf) {
+ LOGP(DRLCMAC, LOGL_ERROR, "Got ACK, but UL "
+ "TBF is gone\n");
+ break;
+ }
+ tbf_new_state(tbf, GPRS_RLCMAC_FLOW);
+ if ((tbf->state_flags &
+ (1 << GPRS_RLCMAC_FLAG_TO_UL_ASS))) {
+ tbf->state_flags &=
+ ~(1 << GPRS_RLCMAC_FLAG_TO_UL_ASS);
+ LOGP(DRLCMAC, LOGL_NOTICE, "Recovered uplink "
+ "assignment\n");
+ }
+ tbf_assign_control_ts(tbf);
break;
}
LOGP(DRLCMAC, LOGL_ERROR, "Error: received PACET CONTROL ACK "
@@ -171,54 +351,39 @@ int gprs_rlcmac_rcv_control_block(bitvec *rlc_block, uint32_t fn)
break;
case MT_PACKET_DOWNLINK_ACK_NACK:
tfi = ul_control_block->u.Packet_Downlink_Ack_Nack.DOWNLINK_TFI;
- tbf = tbf_by_poll_fn(fn);
+ tbf = tbf_by_poll_fn(fn, trx, ts);
if (!tbf) {
LOGP(DRLCMAC, LOGL_NOTICE, "PACKET DOWNLINK ACK with "
- "unknown FN=%u TBF=%d\n", fn, tfi);
+ "unknown FN=%u TBF=%d (TRX %d TS %d)\n",
+ fn, tfi, trx, ts);
break;
}
+ tbf->state_flags |= (1 << GPRS_RLCMAC_FLAG_DL_ACK);
+ if ((tbf->state_flags & (1 << GPRS_RLCMAC_FLAG_TO_DL_ACK))) {
+ tbf->state_flags &= ~(1 << GPRS_RLCMAC_FLAG_TO_DL_ACK);
+ LOGP(DRLCMAC, LOGL_NOTICE, "Recovered downlink ack\n");
+ }
/* reset N3105 */
- tbf->dir.dl.n3105 = 0;
+ tbf->n3105 = 0;
/* stop timer T3191 */
tbf_timer_stop(tbf);
tlli = tbf->tlli;
LOGP(DRLCMAC, LOGL_DEBUG, "RX: [PCU <- BTS] TFI: %u TLLI: 0x%08x Packet Downlink Ack/Nack\n", tbf->tfi, tbf->tlli);
tbf->poll_state = GPRS_RLCMAC_POLL_NONE;
- gprs_rlcmac_downlink_ack(tbf,
+ rc = gprs_rlcmac_downlink_ack(tbf,
ul_control_block->u.Packet_Downlink_Ack_Nack.Ack_Nack_Description.FINAL_ACK_INDICATION,
ul_control_block->u.Packet_Downlink_Ack_Nack.Ack_Nack_Description.STARTING_SEQUENCE_NUMBER,
ul_control_block->u.Packet_Downlink_Ack_Nack.Ack_Nack_Description.RECEIVED_BLOCK_BITMAP);
+ if (rc == 1) {
+ tbf_free(tbf);
+ break;
+ }
/* check for channel request */
if (ul_control_block->u.Packet_Downlink_Ack_Nack.Exist_Channel_Request_Description) {
- uint8_t trx, ts, usf;
- struct gprs_rlcmac_tbf *ul_tbf;
- struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts;
-
LOGP(DRLCMAC, LOGL_DEBUG, "MS requests UL TBF in ack "
"message, so we provide one:\n");
-uplink_request:
- /* create new tbf */
- tfi = tfi_alloc(&trx, &ts);
- if (tfi < 0) {
- LOGP(DRLCMAC, LOGL_NOTICE, "No PDCH resource\n");
- /* FIXME: send reject */
- break;
- }
- usf = find_free_usf(trx, ts);
- if (usf < 0) {
- LOGP(DRLCMAC, LOGL_NOTICE, "No PDCH resource for USF\n");
- /* FIXME: send reject */
- break;
- }
- ul_tbf = tbf_alloc(tfi, trx, ts);
- ul_tbf->tlli = tbf->tlli;
- ul_tbf->tlli_valid = 1; /* no content resolution */
- ul_tbf->ta = tbf->ta; /* use current TA */
- ul_tbf->direction = GPRS_RLCMAC_UL_TBF;
- ul_tbf->dir.ul.usf = usf;
- tbf_new_state(ul_tbf, GPRS_RLCMAC_FLOW);
- tbf_timer_start(ul_tbf, 3169, bts->t3169, 0);
+ alloc_ul_tbf(tbf->trx, tbf->first_ts, tbf->ms_class, tbf->tlli, tbf->ta, tbf);
/* schedule uplink assignment */
tbf->ul_ass_state = GPRS_RLCMAC_UL_ASS_SEND_ASS;
}
@@ -227,22 +392,55 @@ uplink_request:
if (ul_control_block->u.Packet_Resource_Request.ID.UnionType) {
tlli = ul_control_block->u.Packet_Resource_Request.ID.u.TLLI;
tbf = tbf_by_tlli(tlli, GPRS_RLCMAC_UL_TBF);
+ if (tbf) {
+ LOGP(DRLCMACUL, LOGL_NOTICE, "Got RACH from "
+ "TLLI=0x%08x while UL TBF=%d still "
+ "exists. Killing pending DL TBF\n",
+ tlli, tbf->tfi);
+ tbf_free(tbf);
+ tbf = NULL;
+ }
if (!tbf) {
- LOGP(DRLCMAC, LOGL_NOTICE, "PACKET RESSOURCE REQ unknown uplink TLLI=0x%08x\n", tlli);
+ uint8_t ms_class = 0;
+ struct gprs_rlcmac_tbf *dl_tbf;
+
+ if ((dl_tbf = tbf_by_tlli(tlli, GPRS_RLCMAC_DL_TBF))) {
+ LOGP(DRLCMACUL, LOGL_NOTICE, "Got RACH from "
+ "TLLI=0x%08x while DL TBF=%d still exists. "
+ "Killing pending DL TBF\n", tlli,
+ dl_tbf->tfi);
+ tbf_free(dl_tbf);
+ }
+ LOGP(DRLCMAC, LOGL_DEBUG, "MS requests UL TBF "
+ "in packet ressource request of single "
+ "block, so we provide one:\n");
+ if (ul_control_block->u.Packet_Resource_Request.Exist_MS_Radio_Access_capability)
+ ms_class = get_ms_class_by_capability(&ul_control_block->u.Packet_Resource_Request.MS_Radio_Access_capability);
+ if (!ms_class)
+ LOGP(DRLCMAC, LOGL_NOTICE, "MS does not give us a class.\n");
+ tbf = alloc_ul_tbf(trx, ts, ms_class, tlli, 0, NULL);
+#warning FIXME TA!!!
+ if (!tbf)
+ break;
+ /* set control ts to current MS's TS, until assignment complete */
+ LOGP(DRLCMAC, LOGL_DEBUG, "Change control TS to %d until assinment is complete.\n", ts);
+ tbf->control_ts = ts;
+ /* schedule uplink assignment */
+ tbf->ul_ass_state = GPRS_RLCMAC_UL_ASS_SEND_ASS;
break;
}
tfi = tbf->tfi;
} else {
if (ul_control_block->u.Packet_Resource_Request.ID.u.Global_TFI.UnionType) {
tfi = ul_control_block->u.Packet_Resource_Request.ID.u.Global_TFI.u.DOWNLINK_TFI;
- tbf = tbf_by_tfi(tfi, GPRS_RLCMAC_DL_TBF);
+ tbf = tbf_by_tfi(tfi, trx, GPRS_RLCMAC_DL_TBF);
if (!tbf) {
LOGP(DRLCMAC, LOGL_NOTICE, "PACKET RESSOURCE REQ unknown downlink TBF=%d\n", tlli);
break;
}
} else {
tfi = ul_control_block->u.Packet_Resource_Request.ID.u.Global_TFI.u.UPLINK_TFI;
- tbf = tbf_by_tfi(tfi, GPRS_RLCMAC_UL_TBF);
+ tbf = tbf_by_tfi(tfi, trx, GPRS_RLCMAC_UL_TBF);
if (!tbf) {
LOGP(DRLCMAC, LOGL_NOTICE, "PACKET RESSOURCE REQ unknown uplink TBF=%d\n", tlli);
break;
@@ -250,16 +448,12 @@ uplink_request:
}
tlli = tbf->tlli;
}
- LOGP(DRLCMAC, LOGL_DEBUG, "RX: [PCU <- BTS] TFI: %u TLLI: 0x%08x Packet resource request\n", tbf->tfi, tbf->tlli);
-#warning FIXME
-puts("FIXME: UL request during UL request"); exit(0);
-
-
+ LOGP(DRLCMAC, LOGL_ERROR, "RX: [PCU <- BTS] %s TFI: %u TLLI: 0x%08x FIXME: Packet ressource request\n", (tbf->direction == GPRS_RLCMAC_UL_TBF) ? "UL" : "DL", tbf->tfi, tbf->tlli);
break;
default:
LOGP(DRLCMAC, LOGL_NOTICE, "RX: [PCU <- BTS] unknown control block received\n");
}
- free(ul_control_block);
+ talloc_free(ul_control_block);
return 1;
}
@@ -271,7 +465,8 @@ void tbf_timer_cb(void *_tbf)
{
struct gprs_rlcmac_tbf *tbf = (struct gprs_rlcmac_tbf *)_tbf;
- LOGP(DRLCMAC, LOGL_DEBUG, "TBF=%d timer %u expired.\n", tbf->tfi,
+ LOGP(DRLCMAC, LOGL_DEBUG, "%s TBF=%d timer %u expired.\n",
+ (tbf->direction == GPRS_RLCMAC_UL_TBF) ? "UL" : "DL", tbf->tfi,
tbf->T);
tbf->num_T_exp++;
@@ -279,22 +474,36 @@ void tbf_timer_cb(void *_tbf)
switch (tbf->T) {
#ifdef DEBUG_DL_ASS_IDLE
case 1234:
- gprs_rlcmac_trigger_downlink_assignment(tbf, 0, debug_imsi);
+ gprs_rlcmac_trigger_downlink_assignment(tbf, NULL, debug_imsi);
break;
#endif
case 0: /* assignment */
- /* change state to FLOW, so scheduler will start transmission */
- if (tbf->state == GPRS_RLCMAC_ASSIGN)
- tbf_new_state(tbf, GPRS_RLCMAC_FLOW);
- else
- LOGP(DRLCMAC, LOGL_ERROR, "Error: TBF is not in assign "
- "state\n");
+ if ((tbf->state_flags & (1 << GPRS_RLCMAC_FLAG_PACCH))) {
+ if (tbf->state == GPRS_RLCMAC_ASSIGN) {
+ LOGP(DRLCMAC, LOGL_NOTICE, "Releasing due to "
+ "PACCH assignment timeout.\n");
+ tbf_free(tbf);
+ } else
+ LOGP(DRLCMAC, LOGL_ERROR, "Error: TBF is not "
+ "in assign state\n");
+ }
+ if ((tbf->state_flags & (1 << GPRS_RLCMAC_FLAG_CCCH))) {
+ /* change state to FLOW, so scheduler will start transmission */
+ tbf->dir.dl.wait_confirm = 0;
+ if (tbf->state == GPRS_RLCMAC_ASSIGN) {
+ tbf_new_state(tbf, GPRS_RLCMAC_FLOW);
+ tbf_assign_control_ts(tbf);
+ } else
+ LOGP(DRLCMAC, LOGL_NOTICE, "Continue flow after "
+ "IMM.ASS confirm\n");
+ }
break;
case 3169:
case 3191:
case 3195:
LOGP(DRLCMAC, LOGL_NOTICE, "TBF T%d timeout during "
"transsmission\n", tbf->T);
+ gprs_rlcmac_diag(tbf);
/* fall through */
case 3193:
LOGP(DRLCMAC, LOGL_DEBUG, "TBF will be freed due to timeout\n");
@@ -495,8 +704,7 @@ static int gprs_rlcmac_assemble_llc(struct gprs_rlcmac_tbf *tbf, uint8_t *data,
if (i != frames - 1) {
/* send frame to SGSN */
LOGP(DRLCMACUL, LOGL_INFO, "Complete UL frame for "
- "TBF=%d: %s\n", tbf->tfi,
- osmo_hexdump(tbf->llc_frame, tbf->llc_index));
+ "TBF=%d: len=%d\n", tbf->tfi, tbf->llc_index);
gprs_rlcmac_tx_ul_ud(tbf);
tbf->llc_index = 0; /* reset frame space */
/* also check if CV==0, because the frame may fill up the
@@ -507,8 +715,7 @@ static int gprs_rlcmac_assemble_llc(struct gprs_rlcmac_tbf *tbf, uint8_t *data,
/* send frame to SGSN */
LOGP(DRLCMACUL, LOGL_INFO, "Complete UL frame for "
"TBF=%d that fits precisely in last block: "
- "%s\n", tbf->tfi,
- osmo_hexdump(tbf->llc_frame, tbf->llc_index));
+ "len=%d\n", tbf->tfi, tbf->llc_index);
gprs_rlcmac_tx_ul_ud(tbf);
tbf->llc_index = 0; /* reset frame space */
}
@@ -523,11 +730,18 @@ struct msgb *gprs_rlcmac_send_uplink_ack(struct gprs_rlcmac_tbf *tbf,
int final = (tbf->state == GPRS_RLCMAC_FINISHED);
struct msgb *msg;
- if (final && tbf->poll_state != GPRS_RLCMAC_POLL_NONE) {
- LOGP(DRLCMACUL, LOGL_DEBUG, "Polling is already "
- "sheduled for TBF=%d, so we must wait for final uplink "
- "ack...\n", tbf->tfi);
+ if (final) {
+ if (tbf->poll_state != GPRS_RLCMAC_POLL_NONE) {
+ LOGP(DRLCMACUL, LOGL_DEBUG, "Polling is already "
+ "sheduled for TBF=%d, so we must wait for "
+ "final uplink ack...\n", tbf->tfi);
+ return NULL;
+ }
+ if (sba_find(tbf->trx, tbf->control_ts, (fn + 13) % 2715648)) {
+ LOGP(DRLCMACUL, LOGL_DEBUG, "Polling is already "
+ "scheduled for single block allocation...\n");
return NULL;
+ }
}
msg = msgb_alloc(23, "rlcmac_ul_ack");
@@ -540,18 +754,23 @@ struct msgb *gprs_rlcmac_send_uplink_ack(struct gprs_rlcmac_tbf *tbf,
}
bitvec_unhex(ack_vec,
"2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b");
- RlcMacDownlink_t * mac_control_block = (RlcMacDownlink_t *)malloc(sizeof(RlcMacDownlink_t));
+ RlcMacDownlink_t * mac_control_block = (RlcMacDownlink_t *)talloc_zero(tall_pcu_ctx, RlcMacDownlink_t);
write_packet_uplink_ack(mac_control_block, tbf, final);
encode_gsm_rlcmac_downlink(ack_vec, mac_control_block);
bitvec_pack(ack_vec, msgb_put(msg, 23));
bitvec_free(ack_vec);
- free(mac_control_block);
+ talloc_free(mac_control_block);
+
+ /* now we must set this flag, so we are allowed to assign downlink
+ * TBF on PACCH. it is only allowed when TLLI is aknowledged. */
+ tbf->dir.ul.contention_resolution_done = 1;
if (final) {
tbf->poll_state = GPRS_RLCMAC_POLL_SCHED;
tbf->poll_fn = (fn + 13) % 2715648;
/* waiting for final acknowledge */
tbf->ul_ack_state = GPRS_RLCMAC_UL_ACK_WAIT_ACK;
+ tbf->dir.ul.final_ack_sent = 1;
} else
tbf->ul_ack_state = GPRS_RLCMAC_UL_ACK_NONE;
@@ -562,7 +781,8 @@ struct msgb *gprs_rlcmac_send_uplink_ack(struct gprs_rlcmac_tbf *tbf,
*
* The blocks are defragmented and forwarded as LLC frames, if complete.
*/
-int gprs_rlcmac_rcv_data_block_acknowledged(uint8_t *data, uint8_t len)
+int gprs_rlcmac_rcv_data_block_acknowledged(uint8_t trx, uint8_t ts,
+ uint8_t *data, uint8_t len)
{
struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts;
struct gprs_rlcmac_tbf *tbf;
@@ -592,24 +812,21 @@ int gprs_rlcmac_rcv_data_block_acknowledged(uint8_t *data, uint8_t len)
}
/* find TBF inst from given TFI */
- tbf = tbf_by_tfi(rh->tfi, GPRS_RLCMAC_UL_TBF);
+ tbf = tbf_by_tfi(rh->tfi, trx, GPRS_RLCMAC_UL_TBF);
if (!tbf) {
LOGP(DRLCMACUL, LOGL_NOTICE, "UL DATA unknown TBF=%d\n",
rh->tfi);
return 0;
}
-
- if (tbf->direction != GPRS_RLCMAC_UL_TBF) {
- LOGP(DRLCMACUL, LOGL_NOTICE, "UL DATA TBF=%d not Uplink "
- "tbf\n", rh->tfi);
- return 0;
- }
+ tbf->state_flags |= (1 << GPRS_RLCMAC_FLAG_UL_DATA);
LOGP(DRLCMACUL, LOGL_DEBUG, "UL DATA TBF=%d received (V(Q)=%d .. "
"V(R)=%d)\n", rh->tfi, tbf->dir.ul.v_q, tbf->dir.ul.v_r);
/* get TLLI */
if (!tbf->tlli_valid) {
+ struct gprs_rlcmac_tbf *dl_tbf;
+
/* no TLLI yet */
if (!rh->ti) {
LOGP(DRLCMACUL, LOGL_NOTICE, "UL DATA TBF=%d without "
@@ -625,6 +842,13 @@ int gprs_rlcmac_rcv_data_block_acknowledged(uint8_t *data, uint8_t len)
tbf->tlli_valid = 1;
LOGP(DRLCMACUL, LOGL_INFO, "Decoded premier TLLI=0x%08x of "
"UL DATA TBF=%d.\n", tbf->tlli, rh->tfi);
+ if ((dl_tbf = tbf_by_tlli(tbf->tlli, GPRS_RLCMAC_DL_TBF))) {
+ LOGP(DRLCMACUL, LOGL_NOTICE, "Got RACH from "
+ "TLLI=0x%08x while DL TBF=%d still exists. "
+ "Killing pending DL TBF\n", tbf->tlli,
+ dl_tbf->tfi);
+ tbf_free(dl_tbf);
+ }
/* already have TLLI, but we stille get another one */
} else if (rh->ti) {
uint32_t tlli;
@@ -718,9 +942,9 @@ int gprs_rlcmac_rcv_data_block_acknowledged(uint8_t *data, uint8_t len)
/* If TLLI is included or if we received half of the window, we send
* an ack/nack */
if (rh->si || rh->ti || tbf->state == GPRS_RLCMAC_FINISHED
- || (tbf->dir.ul.rx_counter % ACK_AFTER_FRAMES) == 0) {
+ || (tbf->dir.ul.rx_counter % SEND_ACK_AFTER_FRAMES) == 0) {
if (rh->si) {
- LOGP(DRLCMACUL, LOGL_DEBUG, "- Scheduling Ack/Nack, "
+ LOGP(DRLCMACUL, LOGL_NOTICE, "- Scheduling Ack/Nack, "
"because MS is stalled.\n");
}
if (rh->ti) {
@@ -731,10 +955,10 @@ int gprs_rlcmac_rcv_data_block_acknowledged(uint8_t *data, uint8_t len)
LOGP(DRLCMACUL, LOGL_DEBUG, "- Scheduling Ack/Nack, "
"because last block has CV==0.\n");
}
- if ((tbf->dir.ul.rx_counter % ACK_AFTER_FRAMES) == 0) {
+ if ((tbf->dir.ul.rx_counter % SEND_ACK_AFTER_FRAMES) == 0) {
LOGP(DRLCMACUL, LOGL_DEBUG, "- Scheduling Ack/Nack, "
"because %d frames received.\n",
- ACK_AFTER_FRAMES);
+ SEND_ACK_AFTER_FRAMES);
}
if (tbf->ul_ack_state == GPRS_RLCMAC_UL_ACK_NONE) {
/* trigger sending at next RTS */
@@ -752,15 +976,23 @@ int gprs_rlcmac_rcv_data_block_acknowledged(uint8_t *data, uint8_t len)
struct msgb *gprs_rlcmac_send_packet_uplink_assignment(
struct gprs_rlcmac_tbf *tbf, uint32_t fn)
{
+ struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts;
struct msgb *msg;
struct gprs_rlcmac_tbf *new_tbf;
+#if POLLING_ASSIGNMENT_UL == 1
if (tbf->poll_state != GPRS_RLCMAC_POLL_NONE) {
LOGP(DRLCMACUL, LOGL_DEBUG, "Polling is already "
"sheduled for TBF=%d, so we must wait for uplink "
"assignment...\n", tbf->tfi);
return NULL;
}
+ if (sba_find(tbf->trx, tbf->control_ts, (fn + 13) % 2715648)) {
+ LOGP(DRLCMACUL, LOGL_DEBUG, "Polling is already scheduled for "
+ "single block allocation...\n");
+ return NULL;
+ }
+#endif
/* on down TBF we get the uplink TBF to be assigned. */
if (tbf->direction == GPRS_RLCMAC_DL_TBF)
@@ -788,24 +1020,26 @@ struct msgb *gprs_rlcmac_send_packet_uplink_assignment(
bitvec_unhex(ass_vec,
"2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b");
write_packet_uplink_assignment(ass_vec, tbf->tfi,
- (tbf->direction == GPRS_RLCMAC_DL_TBF), 0, 0, new_tbf->tfi,
- new_tbf->dir.ul.usf, new_tbf->arfcn, new_tbf->ts, new_tbf->ta,
- new_tbf->tsc, POLLING_ASSIGNMENT);
+ (tbf->direction == GPRS_RLCMAC_DL_TBF), tbf->tlli,
+ tbf->tlli_valid, new_tbf, POLLING_ASSIGNMENT_UL, bts->alpha,
+ bts->gamma);
bitvec_pack(ass_vec, msgb_put(msg, 23));
- RlcMacDownlink_t * mac_control_block = (RlcMacDownlink_t *)malloc(sizeof(RlcMacDownlink_t));
+ RlcMacDownlink_t * mac_control_block = (RlcMacDownlink_t *)talloc_zero(tall_pcu_ctx, RlcMacDownlink_t);
LOGP(DRLCMAC, LOGL_DEBUG, "+++++++++++++++++++++++++ TX : Packet Uplink Assignment +++++++++++++++++++++++++\n");
decode_gsm_rlcmac_downlink(ass_vec, mac_control_block);
LOGPC(DCSN1, LOGL_NOTICE, "\n");
LOGP(DRLCMAC, LOGL_DEBUG, "------------------------- TX : Packet Uplink Assignment -------------------------\n");
bitvec_free(ass_vec);
+ talloc_free(mac_control_block);
-#if POLLING_ASSIGNMENT == 1
- FIXME process does not work, also the acknowledgement is not checked.
+#if POLLING_ASSIGNMENT_UL == 1
tbf->poll_state = GPRS_RLCMAC_POLL_SCHED;
tbf->poll_fn = (fn + 13) % 2715648;
tbf->ul_ass_state = GPRS_RLCMAC_UL_ASS_WAIT_ACK;
#else
tbf->ul_ass_state = GPRS_RLCMAC_UL_ASS_NONE;
+ tbf_new_state(new_tbf, GPRS_RLCMAC_FLOW);
+ tbf_assign_control_ts(new_tbf);
#endif
return msg;
@@ -816,39 +1050,76 @@ int gprs_rlcmac_rcv_rach(uint8_t ra, uint32_t Fn, int16_t qta)
struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts;
struct gprs_rlcmac_tbf *tbf;
uint8_t trx, ts;
- int tfi, usf; /* must be signed */
+ int8_t tfi; /* must be signed */
+ uint8_t sb = 0;
+ uint32_t sb_fn = 0;
+ int rc;
+ uint8_t plen;
LOGP(DRLCMAC, LOGL_DEBUG, "MS requests UL TBF on RACH, so we provide "
"one:\n");
- // Create new TBF
- tfi = tfi_alloc(&trx, &ts);
- if (tfi < 0) {
- LOGP(DRLCMAC, LOGL_NOTICE, "No PDCH resource\n");
- /* FIXME: send reject */
- return -EBUSY;
- }
- usf = find_free_usf(trx, ts);
- if (usf < 0) {
- LOGP(DRLCMAC, LOGL_NOTICE, "No PDCH resource for USF\n");
- /* FIXME: send reject */
- return -EBUSY;
+ if ((ra & 0xf8) == 0x70) {
+ LOGP(DRLCMAC, LOGL_DEBUG, "MS requests single block "
+ "allocation\n");
+ sb = 1;
+ } else if (bts->force_two_phase) {
+ LOGP(DRLCMAC, LOGL_DEBUG, "MS requests single phase access, "
+ "but we force two phase access\n");
+ sb = 1;
}
- tbf = tbf_alloc(tfi, trx, ts);
if (qta < 0)
qta = 0;
if (qta > 252)
qta = 252;
- tbf->ta = qta >> 2;
- tbf->direction = GPRS_RLCMAC_UL_TBF;
- tbf->dir.ul.usf = usf;
- tbf_new_state(tbf, GPRS_RLCMAC_FLOW);
- tbf_timer_start(tbf, 3169, bts->t3169, 0);
- LOGP(DRLCMAC, LOGL_DEBUG, "TBF: [UPLINK] START TFI: %u\n", tbf->tfi);
- LOGP(DRLCMAC, LOGL_DEBUG, "RX: [PCU <- BTS] TFI: %u RACH qbit-ta=%d ra=%d, Fn=%d (%d,%d,%d)\n", tbf->tfi, qta, ra, Fn, (Fn / (26 * 51)) % 32, Fn % 51, Fn % 26);
- LOGP(DRLCMAC, LOGL_INFO, "TX: START TFI: %u Immediate Assignment Uplink (AGCH)\n", tbf->tfi);
+ if (sb) {
+ rc = sba_alloc(&trx, &ts, &sb_fn, qta >> 2);
+ if (rc < 0)
+ return rc;
+ LOGP(DRLCMAC, LOGL_DEBUG, "RX: [PCU <- BTS] RACH qbit-ta=%d "
+ "ra=0x%02x, Fn=%d (%d,%d,%d)\n", qta, ra, Fn,
+ (Fn / (26 * 51)) % 32, Fn % 51, Fn % 26);
+ LOGP(DRLCMAC, LOGL_INFO, "TX: Immediate Assignment Uplink "
+ "(AGCH)\n");
+ } else {
+ // Create new TBF
+ tfi = tfi_alloc(GPRS_RLCMAC_UL_TBF, &trx, &ts, -1, -1);
+ if (tfi < 0) {
+ LOGP(DRLCMAC, LOGL_NOTICE, "No PDCH ressource\n");
+ /* FIXME: send reject */
+ return -EBUSY;
+ }
+ /* set class to 0, since we don't know the multislot class yet */
+ tbf = tbf_alloc(NULL, GPRS_RLCMAC_UL_TBF, tfi, trx, ts, 0, 1);
+ if (!tbf) {
+ LOGP(DRLCMAC, LOGL_NOTICE, "No PDCH ressource\n");
+ /* FIXME: send reject */
+ return -EBUSY;
+ }
+ tbf->ta = qta >> 2;
+ tbf_new_state(tbf, GPRS_RLCMAC_FLOW);
+ tbf->state_flags |= (1 << GPRS_RLCMAC_FLAG_CCCH);
+ tbf_timer_start(tbf, 3169, bts->t3169, 0);
+ LOGP(DRLCMAC, LOGL_DEBUG, "TBF: [UPLINK] START TFI: %u\n",
+ tbf->tfi);
+ LOGP(DRLCMAC, LOGL_DEBUG, "RX: [PCU <- BTS] TFI: %u RACH "
+ "qbit-ta=%d ra=0x%02x, Fn=%d (%d,%d,%d)\n", tbf->tfi,
+ qta, ra, Fn, (Fn / (26 * 51)) % 32, Fn % 51, Fn % 26);
+ LOGP(DRLCMAC, LOGL_INFO, "TX: START TFI: %u Immediate "
+ "Assignment Uplink (AGCH)\n", tbf->tfi);
+ }
bitvec *immediate_assignment = bitvec_alloc(22) /* without plen */;
- bitvec_unhex(immediate_assignment, "2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b");
- int plen = write_immediate_assignment(immediate_assignment, 0, ra, Fn, tbf->ta, tbf->arfcn, tbf->ts, tbf->tsc, tbf->tfi, usf, 0, 0, 0);
+ bitvec_unhex(immediate_assignment,
+ "2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b");
+ if (sb)
+ plen = write_immediate_assignment(immediate_assignment, 0, ra,
+ Fn, qta >> 2, bts->trx[trx].arfcn, ts,
+ bts->trx[trx].pdch[ts].tsc, 0, 0, 0, 0, sb_fn, 1,
+ bts->alpha, bts->gamma);
+ else
+ plen = write_immediate_assignment(immediate_assignment, 0, ra,
+ Fn, tbf->ta, tbf->arfcn, tbf->first_ts, tbf->tsc,
+ tbf->tfi, tbf->dir.ul.usf[tbf->first_ts], 0, 0, 0, 0,
+ bts->alpha, bts->gamma);
pcu_l1if_tx_agch(immediate_assignment, plen);
bitvec_free(immediate_assignment);
@@ -860,12 +1131,72 @@ int gprs_rlcmac_rcv_rach(uint8_t ra, uint32_t Fn, int16_t qta)
* DL data block flow
*/
+static struct msgb *llc_dequeue(struct gprs_rlcmac_tbf *tbf)
+{
+ struct msgb *msg;
+ struct timeval *tv, tv_now;
+ uint32_t octets = 0, frames = 0;
+
+ gettimeofday(&tv_now, NULL);
+
+ while ((msg = msgb_dequeue(&tbf->llc_queue))) {
+ tv = (struct timeval *)msg->data;
+ msgb_pull(msg, sizeof(*tv));
+ if (tv->tv_sec /* not infinite */
+ && (tv_now.tv_sec > tv->tv_sec /* and secs expired */
+ || (tv_now.tv_sec == tv->tv_sec /* .. or if secs equal .. */
+ && tv_now.tv_usec > tv->tv_usec))) { /* .. usecs expired */
+ LOGP(DRLCMACDL, LOGL_NOTICE, "Discarding LLC PDU of "
+ "DL TBF=%d, because lifetime limit reached\n",
+ tbf->tfi);
+ frames++;
+ octets += msg->len;
+ msgb_free(msg);
+ continue;
+ }
+ break;
+ }
+
+ if (frames) {
+ if (frames > 0xff)
+ frames = 0xff;
+ if (octets > 0xffffff)
+ octets = 0xffffff;
+ bssgp_tx_llc_discarded(bctx, tbf->tlli, frames, octets);
+ }
+
+ return msg;
+}
+
+static int gprs_rlcmac_debug_bw(struct gprs_rlcmac_tbf *tbf, uint16_t octets)
+{
+ struct timeval now_tv, *bw_tv = &tbf->bw_tv;
+ uint32_t elapsed;
+
+ tbf->bw_octets += octets;
+
+ gettimeofday(&now_tv, NULL);
+ elapsed = ((now_tv.tv_sec - bw_tv->tv_sec) << 7)
+ + ((now_tv.tv_usec - bw_tv->tv_usec) << 7) / 1000000;
+ if (elapsed < 128)
+ return 0;
+
+ LOGP(DRLCMACBW, LOGL_DEBUG, "DL Bandwitdh of TLLI=0x%08x: %d KBits/s\n",
+ tbf->tlli, tbf->bw_octets / elapsed);
+
+ /* reset bandwidth values timestamp */
+ memcpy(bw_tv, &now_tv, sizeof(struct timeval));
+ tbf->bw_octets = 0;
+
+ return 0;
+}
+
/* send DL data block
*
* The messages are fragmented and forwarded as data blocks.
*/
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 gprs_rlcmac_bts *bts = gprs_rlcmac_bts;
struct rlc_dl_header *rh;
@@ -880,6 +1211,7 @@ struct msgb *gprs_rlcmac_send_data_block_acknowledged(
uint8_t *delimiter, *data, *e_pointer;
uint8_t len;
uint16_t space, chunk;
+ int first_fin_ack;
LOGP(DRLCMACDL, LOGL_DEBUG, "DL DATA TBF=%d downlink (V(A)==%d .. "
"V(S)==%d)\n", tbf->tfi, tbf->dir.dl.v_a, tbf->dir.dl.v_s);
@@ -910,7 +1242,7 @@ do_resend:
"because all blocks have been transmitted.\n",
tbf->dir.dl.v_a);
else
- LOGP(DRLCMACDL, LOGL_DEBUG, "- Restarting at BSN %d, "
+ LOGP(DRLCMACDL, LOGL_NOTICE, "- Restarting at BSN %d, "
"because all window is stalled.\n",
tbf->dir.dl.v_a);
/* If V(S) == V(A) and finished state, we would have received
@@ -920,9 +1252,8 @@ do_resend:
* indication from MS. This should never happen if MS works
* correctly. */
if (tbf->dir.dl.v_s == tbf->dir.dl.v_a) {
- LOGP(DRLCMACDL, LOGL_ERROR, "- MS acked all block "
- "(including final block), but did not include "
- "FINAL_ACK_INDICATION!\n");
+ LOGP(DRLCMACDL, LOGL_ERROR, "- MS acked all block, "
+ "but we still transmitting!\n");
/* we just send final block again */
index = ((tbf->dir.dl.v_s - 1) & mod_sns_half);
goto tx_block;
@@ -957,23 +1288,13 @@ do_resend:
/* now we still have untransmitted LLC data, so we fill mac block */
index = tbf->dir.dl.v_s & mod_sns_half;
data = tbf->rlc_block[index];
- switch (bts->initial_cs) {
- case 2: /* CS-2 */
- block_length = 34;
- block_data = 33;
- break;
- case 3: /* CS-3 */
- block_length = 40;
- block_data = 39;
- break;
- case 4: /* CS-4 */
- block_length = 54;
- block_data = 53;
- break;
- default: /* CS-1 */
- block_length = 23;
- block_data = 23;
+ if (tbf->cs == 0) {
+ tbf->cs = bts->initial_cs_dl;
+ if (tbf->cs < 1 || tbf->cs > 4)
+ tbf->cs = 1;
}
+ block_length = gprs_rlcmac_cs[tbf->cs].block_length;
+ block_data = gprs_rlcmac_cs[tbf->cs].block_data;
memset(data, 0x2b, block_data); /* spare bits will be left 0 */
rh = (struct rlc_dl_header *)data;
rh->pt = 0; /* Data Block */
@@ -1013,8 +1334,8 @@ do_resend:
"header, and we are done\n", chunk, space);
LOGP(DRLCMACDL, LOGL_INFO, "Complete DL frame for "
"TBF=%d that fits precisely in last block: "
- "%s\n", tbf->tfi,
- osmo_hexdump(tbf->llc_frame, tbf->llc_length));
+ "len=%d\n", tbf->tfi, tbf->llc_length);
+ gprs_rlcmac_debug_bw(tbf, tbf->llc_length);
/* block is filled, so there is no extension */
*e_pointer |= 0x01;
/* fill space */
@@ -1072,13 +1393,13 @@ do_resend:
memcpy(data, tbf->llc_frame + tbf->llc_index, chunk);
data += chunk;
space -= chunk;
- LOGP(DRLCMACDL, LOGL_INFO, "Complete DL frame for TBF=%d: %s\n",
- tbf->tfi,
- osmo_hexdump(tbf->llc_frame, tbf->llc_length));
+ LOGP(DRLCMACDL, LOGL_INFO, "Complete DL frame for TBF=%d: "
+ "len=%d\n", tbf->tfi, tbf->llc_length);
+ gprs_rlcmac_debug_bw(tbf, tbf->llc_length);
/* reset LLC frame */
tbf->llc_index = tbf->llc_length = 0;
/* dequeue next LLC frame, if any */
- msg = msgb_dequeue(&tbf->llc_queue);
+ msg = llc_dequeue(tbf);
if (msg) {
LOGP(DRLCMACDL, LOGL_INFO, "- Dequeue next LLC for "
"TBF=%d (len=%d)\n", tbf->tfi, msg->len);
@@ -1097,6 +1418,8 @@ do_resend:
"done.\n");
li->e = 1; /* we cannot extend */
rh->fbi = 1; /* we indicate final block */
+ first_fin_ack = 1;
+ /* + 1 indicates: first final ack */
tbf_new_state(tbf, GPRS_RLCMAC_FINISHED);
break;
}
@@ -1121,28 +1444,37 @@ tx_block:
len = tbf->rlc_block_len[index];
rh = (struct rlc_dl_header *)data;
- /* Increment TX-counter */
- tbf->dir.dl.tx_counter++;
-
/* Clear Polling, if still set in history buffer */
rh->s_p = 0;
- /* poll after ACK_AFTER_FRAMES frames, or when final block is tx. */
- if (rh->fbi == 1 || (tbf->dir.dl.tx_counter % ACK_AFTER_FRAMES) == 0) {
- if (rh->fbi == 1) {
+ /* poll after POLL_ACK_AFTER_FRAMES frames, or when final block is tx.
+ */
+ if (tbf->dir.dl.tx_counter >= POLL_ACK_AFTER_FRAMES || first_fin_ack) {
+ if (first_fin_ack) {
LOGP(DRLCMACDL, LOGL_DEBUG, "- Scheduling Ack/Nack "
- "polling, because final block sent.\n");
- }
- if ((tbf->dir.dl.tx_counter % ACK_AFTER_FRAMES) == 0) {
+ "polling, because first final block sent.\n");
+ } else {
LOGP(DRLCMACDL, LOGL_DEBUG, "- Scheduling Ack/Nack "
"polling, because %d blocks sent.\n",
- ACK_AFTER_FRAMES);
+ POLL_ACK_AFTER_FRAMES);
}
+ /* scheduling not possible, because: */
if (tbf->poll_state != GPRS_RLCMAC_POLL_NONE)
- LOGP(DRLCMACDL, LOGL_DEBUG, "Polling is already "
+ LOGP(DRLCMAC, LOGL_DEBUG, "Polling is already "
"sheduled for TBF=%d, so we must wait for "
"requesting downlink ack\n", tbf->tfi);
+ else if (tbf->control_ts != ts)
+ LOGP(DRLCMAC, LOGL_DEBUG, "Polling cannot be "
+ "sheduled in this TS %d, waiting for "
+ "TS %d\n", ts, tbf->control_ts);
+ else if (sba_find(tbf->trx, ts, (fn + 13) % 2715648))
+ LOGP(DRLCMAC, LOGL_DEBUG, "Polling cannot be "
+ "sheduled, because single block alllocation "
+ "already exists\n");
else {
+ LOGP(DRLCMAC, LOGL_DEBUG, "Polling sheduled in this "
+ "TS %d\n", ts);
+ tbf->dir.dl.tx_counter = 0;
/* start timer whenever we send the final block */
if (rh->fbi == 1)
tbf_timer_start(tbf, 3191, bts->t3191, 0);
@@ -1154,7 +1486,13 @@ tx_block:
/* set polling in header */
rh->rrbp = 0; /* N+13 */
rh->s_p = 1; /* Polling */
+
+ /* Increment TX-counter */
+ tbf->dir.dl.tx_counter++;
}
+ } else {
+ /* Increment TX-counter */
+ tbf->dir.dl.tx_counter++;
}
/* return data block as message */
@@ -1189,8 +1527,8 @@ int gprs_rlcmac_downlink_ack(struct gprs_rlcmac_tbf *tbf, uint8_t final,
}
show_rbb[64] = '\0';
LOGP(DRLCMACDL, LOGL_DEBUG, "- ack: (BSN=%d)\"%s\""
- "(BSN=%d) 1=ACK o=NACK\n", ssn - 64, show_rbb,
- ssn - 1);
+ "(BSN=%d) 1=ACK o=NACK\n", (ssn - 64) & mod_sns,
+ show_rbb, (ssn - 1) & mod_sns);
/* apply received array to receive state (SSN-64..SSN-1) */
/* calculate distance of ssn from V(S) */
@@ -1215,8 +1553,15 @@ int gprs_rlcmac_downlink_ack(struct gprs_rlcmac_tbf *tbf, uint8_t final,
}
}
} else {
- LOGP(DRLCMACDL, LOGL_DEBUG, "- ack range is out of "
- "V(A)..V(S) range\n");
+ /* this might happpen, if the downlink assignment
+ * was not received by ms and the ack refers
+ * to previous TBF
+ * FIXME: we should implement polling for
+ * control ack!*/
+ LOGP(DRLCMACDL, LOGL_NOTICE, "- ack range is out of "
+ "V(A)..V(S) range (DL TBF=%d) Free TFB!\n",
+ tbf->tfi);
+ return 1; /* indicate to free TBF */
}
/* raise V(A), if possible */
@@ -1244,13 +1589,17 @@ int gprs_rlcmac_downlink_ack(struct gprs_rlcmac_tbf *tbf, uint8_t final,
"X=Resend-Unacked\n", tbf->dir.dl.v_a, show_v_b,
(tbf->dir.dl.v_s - 1) & mod_sns);
- return 0;
- }
-
- LOGP(DRLCMACDL, LOGL_DEBUG, "- Final ACK received.\n");
+ if (tbf->state == GPRS_RLCMAC_FINISHED
+ && tbf->dir.dl.v_s == tbf->dir.dl.v_a) {
+ LOGP(DRLCMACDL, LOGL_NOTICE, "Received final block, "
+ "but without final ack inidcation\n");
+ } else
+ return 0;
+ } else
+ LOGP(DRLCMACDL, LOGL_DEBUG, "- Final ACK received.\n");
/* check for LLC PDU in the LLC Queue */
- msg = msgb_dequeue(&tbf->llc_queue);
+ msg = llc_dequeue(tbf);
if (!msg) {
struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts;
@@ -1259,7 +1608,7 @@ int gprs_rlcmac_downlink_ack(struct gprs_rlcmac_tbf *tbf, uint8_t final,
"release.\n");
/* start T3193 */
tbf_timer_start(tbf, 3193, bts->t3193_msec / 1000,
- bts->t3193_msec & 1000);
+ (bts->t3193_msec % 1000) * 1000);
tbf_new_state(tbf, GPRS_RLCMAC_WAIT_RELEASE);
return 0;
@@ -1274,7 +1623,10 @@ int gprs_rlcmac_downlink_ack(struct gprs_rlcmac_tbf *tbf, uint8_t final,
LOGP(DRLCMAC, LOGL_DEBUG, "Trigger dowlink assignment on PACCH, "
"because another LLC PDU has arrived in between\n");
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);
+ tbf_update(tbf);
+ gprs_rlcmac_trigger_downlink_assignment(tbf, tbf, NULL);
return 0;
}
@@ -1283,20 +1635,43 @@ int gprs_rlcmac_downlink_ack(struct gprs_rlcmac_tbf *tbf, uint8_t final,
struct msgb *gprs_rlcmac_send_packet_downlink_assignment(
struct gprs_rlcmac_tbf *tbf, uint32_t fn)
{
+ struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts;
struct msgb *msg;
struct gprs_rlcmac_tbf *new_tbf;
+ int poll_ass_dl = POLLING_ASSIGNMENT_DL;
- if (tbf->poll_state != GPRS_RLCMAC_POLL_NONE) {
- LOGP(DRLCMACDL, LOGL_DEBUG, "Polling is already "
- "sheduled for TBF=%d, so we must wait for downlink "
- "assignment...\n", tbf->tfi);
+ if (poll_ass_dl && tbf->direction == GPRS_RLCMAC_DL_TBF
+ && tbf->control_ts != tbf->first_common_ts) {
+ LOGP(DRLCMAC, LOGL_NOTICE, "Cannot poll for downlink "
+ "assigment, because MS cannot reply.\n");
+ poll_ass_dl = 0;
+ }
+ if (poll_ass_dl) {
+ if (tbf->poll_state != GPRS_RLCMAC_POLL_NONE) {
+ LOGP(DRLCMAC, LOGL_DEBUG, "Polling is already sheduled "
+ "for TBF=%d, so we must wait for downlink "
+ "assignment...\n", tbf->tfi);
+ return NULL;
+ }
+ if (sba_find(tbf->trx, tbf->control_ts, (fn + 13) % 2715648)) {
+ LOGP(DRLCMACUL, LOGL_DEBUG, "Polling is already "
+ "scheduled for single block allocation...\n");
return NULL;
+ }
}
/* on uplink TBF we get the downlink TBF to be assigned. */
- if (tbf->direction == GPRS_RLCMAC_UL_TBF)
+ if (tbf->direction == GPRS_RLCMAC_UL_TBF) {
+ /* be sure to check first, if contention resolution is done,
+ * otherwise we cannot send the assignment yet */
+ if (!tbf->dir.ul.contention_resolution_done) {
+ LOGP(DRLCMAC, LOGL_DEBUG, "Cannot assign DL TBF now, "
+ "because contention resolution is not "
+ "finished.\n");
+ return NULL;
+ }
new_tbf = tbf_by_tlli(tbf->tlli, GPRS_RLCMAC_DL_TBF);
- else
+ } else
new_tbf = tbf;
if (!new_tbf) {
LOGP(DRLCMACDL, LOGL_ERROR, "We have a schedule for downlink "
@@ -1317,26 +1692,30 @@ struct msgb *gprs_rlcmac_send_packet_downlink_assignment(
bitvec_unhex(ass_vec,
"2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b");
LOGP(DRLCMAC, LOGL_INFO, "TBF: START TFI: %u TLLI: 0x%08x Packet Downlink Assignment (PACCH)\n", new_tbf->tfi, new_tbf->tlli);
- RlcMacDownlink_t * mac_control_block = (RlcMacDownlink_t *)malloc(sizeof(RlcMacDownlink_t));
+ RlcMacDownlink_t * mac_control_block = (RlcMacDownlink_t *)talloc_zero(tall_pcu_ctx, RlcMacDownlink_t);
write_packet_downlink_assignment(mac_control_block, tbf->tfi,
- (tbf->direction == GPRS_RLCMAC_DL_TBF), new_tbf->tfi,
- new_tbf->arfcn, new_tbf->ts, new_tbf->ta, new_tbf->tsc,
- POLLING_ASSIGNMENT);
+ (tbf->direction == GPRS_RLCMAC_DL_TBF), new_tbf,
+ poll_ass_dl, bts->alpha, bts->gamma);
LOGP(DRLCMAC, LOGL_DEBUG, "+++++++++++++++++++++++++ TX : Packet Downlink Assignment +++++++++++++++++++++++++\n");
encode_gsm_rlcmac_downlink(ass_vec, mac_control_block);
LOGPC(DCSN1, LOGL_NOTICE, "\n");
LOGP(DRLCMAC, LOGL_DEBUG, "------------------------- TX : Packet Downlink Assignment -------------------------\n");
bitvec_pack(ass_vec, msgb_put(msg, 23));
bitvec_free(ass_vec);
- free(mac_control_block);
+ talloc_free(mac_control_block);
-#if POLLING_ASSIGNMENT == 1
- tbf->poll_state = GPRS_RLCMAC_POLL_SCHED;
- tbf->poll_fn = (fn + 13) % 2715648;
- tbf->dl_ass_state = GPRS_RLCMAC_DL_ASS_WAIT_ACK;
-#else
- tbf->dl_ass_state = GPRS_RLCMAC_DL_ASS_NONE;
-#endif
+ if (poll_ass_dl) {
+ tbf->poll_state = GPRS_RLCMAC_POLL_SCHED;
+ tbf->poll_fn = (fn + 13) % 2715648;
+ tbf->dl_ass_state = GPRS_RLCMAC_DL_ASS_WAIT_ACK;
+ } else {
+ tbf->dl_ass_state = GPRS_RLCMAC_DL_ASS_NONE;
+ tbf_new_state(new_tbf, GPRS_RLCMAC_FLOW);
+ tbf_assign_control_ts(new_tbf);
+ /* stop pending assignment timer */
+ tbf_timer_stop(new_tbf);
+
+ }
return msg;
}
@@ -1344,22 +1723,26 @@ struct msgb *gprs_rlcmac_send_packet_downlink_assignment(
static void gprs_rlcmac_downlink_assignment(gprs_rlcmac_tbf *tbf, uint8_t poll,
char *imsi)
{
+ struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts;
+ int plen;
+
LOGP(DRLCMAC, LOGL_INFO, "TX: START TFI: %u TLLI: 0x%08x Immediate Assignment Downlink (PCH)\n", tbf->tfi, tbf->tlli);
bitvec *immediate_assignment = bitvec_alloc(22); /* without plen */
bitvec_unhex(immediate_assignment, "2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b");
/* use request reference that has maximum distance to current time,
* so the assignment will not conflict with possible RACH requests. */
- int plen = write_immediate_assignment(immediate_assignment, 1, 125, (tbf->pdch->last_rts_fn + 21216) % 2715648, tbf->ta, tbf->arfcn, tbf->ts, tbf->tsc, tbf->tfi, 0, tbf->tlli, poll, tbf->poll_fn);
+ plen = write_immediate_assignment(immediate_assignment, 1, 125,
+ (tbf->pdch[tbf->first_ts]->last_rts_fn + 21216) % 2715648, tbf->ta,
+ tbf->arfcn, tbf->first_ts, tbf->tsc, tbf->tfi, 0, tbf->tlli, poll,
+ tbf->poll_fn, 0, bts->alpha, bts->gamma);
pcu_l1if_tx_pch(immediate_assignment, plen, imsi);
bitvec_free(immediate_assignment);
}
/* depending on the current TBF, we assign on PACCH or AGCH */
-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)
{
- gprs_rlcmac_tbf *old_tbf;
-
#ifdef DEBUG_DL_ASS_IDLE
strncpy(debug_imsi, imsi);
LOGP(DRLCMAC, LOGL_ERROR, "**** DEBUGGING DOWNLINK ASSIGNMENT ****\n");
@@ -1369,10 +1752,6 @@ void gprs_rlcmac_trigger_downlink_assignment(gprs_rlcmac_tbf *tbf,
tbf_timer_stop(tbf);
/* check for downlink tbf: */
- if (old_downlink)
- old_tbf = tbf_by_tlli(tbf->tlli, GPRS_RLCMAC_DL_TBF);
- else
- old_tbf = tbf_by_tlli(tbf->tlli, GPRS_RLCMAC_UL_TBF);
if (old_tbf) {
#ifdef DEBUG_DL_ASS_IDLE
LOGP(DRLCMAC, LOGL_ERROR, "We must wait for current TBF to be "
@@ -1381,14 +1760,15 @@ void gprs_rlcmac_trigger_downlink_assignment(gprs_rlcmac_tbf *tbf,
tbf_timer_start(tbf, 1234, 1,0);
#else
LOGP(DRLCMAC, LOGL_DEBUG, "Send dowlink assignment on "
- "PACCH, because %slink TBF=%d exists for TLLI=0x%08x\n",
- (tbf->direction == GPRS_RLCMAC_DL_TBF) ? "down" : "up",
- old_tbf->tfi, old_tbf->tlli);
+ "PACCH, because %s TBF=%d exists for TLLI=0x%08x\n",
+ (old_tbf->direction == GPRS_RLCMAC_UL_TBF)
+ ? "UL" : "DL", old_tbf->tfi, old_tbf->tlli);
old_tbf->dl_ass_state = GPRS_RLCMAC_DL_ASS_SEND_ASS;
/* use TA from old TBF */
tbf->ta = old_tbf->ta;
/* change state */
tbf_new_state(tbf, GPRS_RLCMAC_ASSIGN);
+ tbf->state_flags |= (1 << GPRS_RLCMAC_FLAG_PACCH);
/* start timer */
tbf_timer_start(tbf, 0, Tassign_pacch);
#endif
@@ -1398,12 +1778,53 @@ void gprs_rlcmac_trigger_downlink_assignment(gprs_rlcmac_tbf *tbf,
LOGP(DRLCMAC, LOGL_ERROR, "No valid IMSI!\n");
return;
}
- /* send immediate assignment */
- gprs_rlcmac_downlink_assignment(tbf, 0, imsi);
/* change state */
tbf_new_state(tbf, GPRS_RLCMAC_ASSIGN);
- /* start timer */
+ tbf->state_flags |= (1 << GPRS_RLCMAC_FLAG_CCCH);
+ strncpy(tbf->dir.dl.imsi, imsi, sizeof(tbf->dir.dl.imsi));
+ /* send immediate assignment */
+ gprs_rlcmac_downlink_assignment(tbf, 0, imsi);
+ tbf->dir.dl.wait_confirm = 1;
+ }
+}
+
+int gprs_rlcmac_imm_ass_cnf(uint8_t *data, uint32_t fn)
+{
+ struct gprs_rlcmac_tbf *tbf;
+ uint8_t plen;
+ uint32_t tlli;
+
+ /* move to IA Rest Octets */
+ plen = data[0] >> 2;
+ data += 1 + plen;
+
+ if ((*data & 0xf0) != 0xd0) {
+ LOGP(DRLCMAC, LOGL_ERROR, "Got IMM.ASS confirm, but rest "
+ "octets do not start with bit sequence 'HH01' "
+ "(Packet Downlink Assignment)\n");
+ return -EINVAL;
+ }
+
+ /* get TLLI from downlink assignment */
+ tlli = (*data++) << 28;
+ tlli |= (*data++) << 20;
+ tlli |= (*data++) << 12;
+ tlli |= (*data++) << 4;
+ tlli |= (*data++) >> 4;
+
+ tbf = tbf_by_tlli(tlli, GPRS_RLCMAC_DL_TBF);
+ if (!tbf) {
+ LOGP(DRLCMAC, LOGL_ERROR, "Got IMM.ASS confirm, but TLLI=%08x "
+ "does not exit\n", tlli);
+ return -EINVAL;
+ }
+
+ LOGP(DRLCMAC, LOGL_DEBUG, "Got IMM.ASS confirm for TLLI=%08x\n", tlli);
+
+ if (tbf->dir.dl.wait_confirm) {
tbf_timer_start(tbf, 0, Tassign_agch);
}
- }
+
+ return 0;
+}
diff --git a/src/gprs_rlcmac_sched.cpp b/src/gprs_rlcmac_sched.cpp
index 7f90c2a4..f588c47d 100644
--- a/src/gprs_rlcmac_sched.cpp
+++ b/src/gprs_rlcmac_sched.cpp
@@ -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);
diff --git a/src/openbts_sock.cpp b/src/openbts_sock.cpp
index cbf7adcc..1f1e9044 100644
--- a/src/openbts_sock.cpp
+++ b/src/openbts_sock.cpp
@@ -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;
diff --git a/src/pcu_l1_if.cpp b/src/pcu_l1_if.cpp
index fd8b3c34..14981820 100644
--- a/src/pcu_l1_if.cpp
+++ b/src/pcu_l1_if.cpp
@@ -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);
diff --git a/src/pcu_main.cpp b/src/pcu_main.cpp
index 6c30c1e4..ee6f70c4 100644
--- a/src/pcu_main.cpp
+++ b/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;
}
diff --git a/src/pcu_vty.c b/src/pcu_vty.c
new file mode 100644
index 00000000..c55a9ee6
--- /dev/null
+++ b/src/pcu_vty.c
@@ -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;
+}
diff --git a/src/pcu_vty.h b/src/pcu_vty.h
new file mode 100644
index 00000000..390d75cd
--- /dev/null
+++ b/src/pcu_vty.h
@@ -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 */
+
diff --git a/src/pcuif_proto.h b/src/pcuif_proto.h
index 3609f79d..c27bb7dc 100644
--- a/src/pcuif_proto.h
+++ b/src/pcuif_proto.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));
diff --git a/src/sysmo_sock.cpp b/src/sysmo_sock.cpp
index 8d83ca20..c4565952 100644
--- a/src/sysmo_sock.cpp
+++ b/src/sysmo_sock.cpp
@@ -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;
}
diff --git a/src/tbf.txt b/src/tbf.txt
index 82bf05ac..025e56cf 100644
--- a/src/tbf.txt
+++ b/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.
+
+