diff options
-rw-r--r-- | COPYING | 339 | ||||
-rw-r--r-- | README | 22 | ||||
-rw-r--r-- | src/Makefile.am | 16 | ||||
-rw-r--r-- | src/gprs_bssgp_pcu.cpp | 223 | ||||
-rw-r--r-- | src/gprs_bssgp_pcu.h | 7 | ||||
-rw-r--r-- | src/gprs_debug.cpp | 1 | ||||
-rw-r--r-- | src/gprs_debug.h | 1 | ||||
-rw-r--r-- | src/gprs_rlcmac.cpp | 1177 | ||||
-rw-r--r-- | src/gprs_rlcmac.h | 180 | ||||
-rw-r--r-- | src/gprs_rlcmac_data.cpp | 841 | ||||
-rw-r--r-- | src/gprs_rlcmac_sched.cpp | 336 | ||||
-rw-r--r-- | src/openbts_sock.cpp | 4 | ||||
-rw-r--r-- | src/pcu_l1_if.cpp | 154 | ||||
-rw-r--r-- | src/pcu_main.cpp | 107 | ||||
-rw-r--r-- | src/pcu_vty.c | 313 | ||||
-rw-r--r-- | src/pcu_vty.h | 22 | ||||
-rw-r--r-- | src/pcuif_proto.h | 25 | ||||
-rw-r--r-- | src/sysmo_sock.cpp | 35 | ||||
-rw-r--r-- | src/tbf.txt | 50 |
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. @@ -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. + + |