pespin has uploaded this change for review.
rlcmac: Introduce DL TBF creation through PCH ImmAss
This patch only introduces the gprs_rlcmac_dl_tbf subclass and
allocates/frees it based on PCH ImmAss. It provides a unit test to
showcase the scenario.
Change-Id: I7f98e3456ef35d80becdad3481afeb771457b0ef
---
M include/osmocom/gprs/rlcmac/Makefile.am
M include/osmocom/gprs/rlcmac/gre.h
M include/osmocom/gprs/rlcmac/rlcmac_private.h
A include/osmocom/gprs/rlcmac/tbf_dl.h
M src/rlcmac/Makefile.am
M src/rlcmac/gre.c
M src/rlcmac/rlcmac.c
A src/rlcmac/tbf_dl.c
M tests/rlcmac/rlcmac_prim_test.c
M tests/rlcmac/rlcmac_prim_test.err
10 files changed, 302 insertions(+), 2 deletions(-)
git pull ssh://gerrit.osmocom.org:29418/libosmo-gprs refs/changes/86/31286/1
diff --git a/include/osmocom/gprs/rlcmac/Makefile.am b/include/osmocom/gprs/rlcmac/Makefile.am
index 3f04ee3..02d1a70 100644
--- a/include/osmocom/gprs/rlcmac/Makefile.am
+++ b/include/osmocom/gprs/rlcmac/Makefile.am
@@ -10,6 +10,7 @@
rlcmac_private.h \
sched.h \
tbf.h \
+ tbf_dl.h \
tbf_ul.h \
tbf_ul_fsm.h \
tbf_ul_ass_fsm.h \
diff --git a/include/osmocom/gprs/rlcmac/gre.h b/include/osmocom/gprs/rlcmac/gre.h
index ea50ddb..ca5f773 100644
--- a/include/osmocom/gprs/rlcmac/gre.h
+++ b/include/osmocom/gprs/rlcmac/gre.h
@@ -4,6 +4,7 @@
#include <osmocom/gprs/rlcmac/rlcmac.h>
#include <osmocom/gprs/rlcmac/llc_queue.h>
+struct gprs_rlcmac_dl_tbf;
struct gprs_rlcmac_ul_tbf;
struct gprs_rlcmac_entity {
@@ -12,6 +13,7 @@
struct gprs_rlcmac_llc_queue *llc_queue;
+ struct gprs_rlcmac_dl_tbf *dl_tbf;
struct gprs_rlcmac_ul_tbf *ul_tbf;
};
diff --git a/include/osmocom/gprs/rlcmac/rlcmac_private.h b/include/osmocom/gprs/rlcmac/rlcmac_private.h
index f35b69a..f9a2f2e 100644
--- a/include/osmocom/gprs/rlcmac/rlcmac_private.h
+++ b/include/osmocom/gprs/rlcmac/rlcmac_private.h
@@ -28,6 +28,16 @@
struct gprs_rlcmac_ul_tbf_allocation_ts ts[8];
};
+struct gprs_rlcmac_dl_tbf_allocation_ts {
+ bool allocated;
+};
+
+struct gprs_rlcmac_dl_tbf_allocation {
+ uint8_t dl_tfi;
+ uint8_t num_ts; /* number of allocated TS */
+ struct gprs_rlcmac_dl_tbf_allocation_ts ts[8];
+};
+
extern int g_rlcmac_log_cat[_OSMO_GPRS_RLCMAC_LOGC_MAX];
#define LOGRLCMAC(lvl, fmt, args...) LOGP(g_rlcmac_log_cat[OSMO_GPRS_RLCMAC_LOGC_RLCMAC], lvl, fmt, ## args)
@@ -56,6 +66,7 @@
struct llist_head gre_list; /* contains (struct gprs_rlcmac_entity)->entry */
uint8_t next_ul_tbf_nr;
+ uint8_t next_dl_tbf_nr;
};
extern struct gprs_rlcmac_ctx *g_ctx;
diff --git a/include/osmocom/gprs/rlcmac/tbf_dl.h b/include/osmocom/gprs/rlcmac/tbf_dl.h
new file mode 100644
index 0000000..ce97ea8
--- /dev/null
+++ b/include/osmocom/gprs/rlcmac/tbf_dl.h
@@ -0,0 +1,52 @@
+/* Downlink TBF, 3GPP TS 44.060 */
+#pragma once
+
+#include <inttypes.h>
+
+#include <osmocom/core/msgb.h>
+
+#include <osmocom/gprs/rlcmac/tbf.h>
+//#include <osmocom/gprs/rlcmac/tbf_dl_fsm.h>
+#include <osmocom/gprs/rlcmac/coding_scheme.h>
+#include <osmocom/gprs/rlcmac/sched.h>
+#include <osmocom/gprs/rlcmac/rlcmac_private.h>
+
+struct gprs_rlcmac_dl_tbf {
+ struct gprs_rlcmac_tbf tbf;
+ //struct gprs_rlcmac_tbf_dl_fsm_ctx state_fsm;
+
+ /* Current TS/TFI/USF allocated by the PCU: */
+ struct gprs_rlcmac_dl_tbf_allocation cur_alloc;
+};
+
+struct gprs_rlcmac_dl_tbf *gprs_rlcmac_dl_tbf_alloc(struct gprs_rlcmac_entity *gre);
+void gprs_rlcmac_dl_tbf_free(struct gprs_rlcmac_dl_tbf *dl_tbf);
+
+int gprs_rlcmac_dl_tbf_configure_l1ctl(struct gprs_rlcmac_dl_tbf *dl_tbf);
+
+static inline struct gprs_rlcmac_tbf *dl_tbf_as_tbf(struct gprs_rlcmac_dl_tbf *dl_tbf)
+{
+ return &dl_tbf->tbf;
+}
+
+static inline const struct gprs_rlcmac_tbf *dl_tbf_as_tbf_const(const struct gprs_rlcmac_dl_tbf *dl_tbf)
+{
+ return &dl_tbf->tbf;
+}
+
+static inline struct gprs_rlcmac_dl_tbf *tbf_as_dl_tbf(struct gprs_rlcmac_tbf *tbf)
+{
+ OSMO_ASSERT(tbf->direction == GPRS_RLCMAC_TBF_DIR_DL);
+ return (struct gprs_rlcmac_dl_tbf *)tbf;
+}
+
+static inline const struct gprs_rlcmac_dl_tbf *tbf_as_dl_tbf_const(struct gprs_rlcmac_tbf *tbf)
+{
+ OSMO_ASSERT(tbf->direction == GPRS_RLCMAC_TBF_DIR_DL);
+ return (const struct gprs_rlcmac_dl_tbf *)tbf;
+}
+
+#define LOGPTBFDL(dl_tbf, lvl, fmt, args...) \
+ LOGP(g_rlcmac_log_cat[OSMO_GPRS_RLCMAC_LOGC_TBFUL], lvl, "TBF(DL:NR-%" PRIu8 ":TLLI-%08x) " fmt, \
+ (dl_tbf)->tbf.nr, (dl_tbf)->tbf.gre->tlli, \
+ ## args)
diff --git a/src/rlcmac/Makefile.am b/src/rlcmac/Makefile.am
index 6fca2b0..bdbb6a4 100644
--- a/src/rlcmac/Makefile.am
+++ b/src/rlcmac/Makefile.am
@@ -39,6 +39,7 @@
rlcmac_prim.c \
sched.c \
tbf.c \
+ tbf_dl.c \
tbf_ul.c \
tbf_ul_fsm.c \
tbf_ul_ass_fsm.c \
diff --git a/src/rlcmac/gre.c b/src/rlcmac/gre.c
index 3d8ba08..40cd585 100644
--- a/src/rlcmac/gre.c
+++ b/src/rlcmac/gre.c
@@ -24,6 +24,7 @@
#include <osmocom/gprs/rlcmac/rlcmac.h>
#include <osmocom/gprs/rlcmac/rlcmac_prim.h>
#include <osmocom/gprs/rlcmac/rlcmac_private.h>
+#include <osmocom/gprs/rlcmac/tbf_dl.h>
#include <osmocom/gprs/rlcmac/tbf_ul_fsm.h>
#include <osmocom/gprs/rlcmac/tbf_ul.h>
#include <osmocom/gprs/rlcmac/gre.h>
@@ -58,6 +59,7 @@
if (!gre)
return;
+ gprs_rlcmac_dl_tbf_free(gre->dl_tbf);
gprs_rlcmac_ul_tbf_free(gre->ul_tbf);
gprs_rlcmac_llc_queue_free(gre->llc_queue);
llist_del(&gre->entry);
diff --git a/src/rlcmac/rlcmac.c b/src/rlcmac/rlcmac.c
index e5a583e..c1a5d76 100644
--- a/src/rlcmac/rlcmac.c
+++ b/src/rlcmac/rlcmac.c
@@ -29,6 +29,7 @@
#include <osmocom/gprs/rlcmac/tbf_ul_fsm.h>
#include <osmocom/gprs/rlcmac/tbf_ul_ass_fsm.h>
#include <osmocom/gprs/rlcmac/gre.h>
+#include <osmocom/gprs/rlcmac/tbf_dl.h>
#include <osmocom/gprs/rlcmac/tbf_ul.h>
#include <osmocom/gprs/rlcmac/csn1_defs.h>
@@ -140,6 +141,40 @@
return rc;
}
+static int gprs_rlcmac_handle_ccch_imm_ass_dl_tbf(uint8_t ts_nr, const struct gsm48_imm_ass *ia, const IA_RestOctets_t *iaro)
+{
+ int rc;
+ struct gprs_rlcmac_entity *gre;
+ struct gprs_rlcmac_dl_tbf *dl_tbf;
+ const Packet_Downlink_ImmAssignment_t *pkdlass;
+
+ if (iaro->UnionType == 1) {
+ /* TODO */
+ return -ENOENT;
+ }
+
+ pkdlass = &iaro->u.hh.u.UplinkDownlinkAssignment.ul_dl.Packet_Downlink_ImmAssignment;
+
+ gre = gprs_rlcmac_find_entity_by_tlli(pkdlass->TLLI);
+ if (!gre) {
+ LOGRLCMAC(LOGL_NOTICE, "Got IMM_ASS (DL_TBF) for unknown TLLI=0x%08x\n", pkdlass->TLLI);
+ return -ENOENT;
+ }
+
+ LOGGRE(gre, LOGL_INFO, "Got PCH IMM_ASS (DL_TBF): DL_TFI=%u TS=%u\n",
+ pkdlass->TFI_ASSIGNMENT, ts_nr);
+ dl_tbf = gprs_rlcmac_dl_tbf_alloc(gre);
+ dl_tbf->cur_alloc.dl_tfi = pkdlass->TFI_ASSIGNMENT;
+ dl_tbf->cur_alloc.ts[ts_nr].allocated = true;
+
+ /* replace old DL TBF with new one: */
+ gprs_rlcmac_dl_tbf_free(gre->dl_tbf);
+ gre->dl_tbf = dl_tbf;
+
+ rc = gprs_rlcmac_dl_tbf_configure_l1ctl(gre->dl_tbf);
+ return rc;
+}
+
int gprs_rlcmac_handle_ccch_imm_ass(const struct gsm48_imm_ass *ia)
{
int rc;
@@ -172,7 +207,7 @@
rc = gprs_rlcmac_handle_ccch_imm_ass_ul_tbf(ch_ts, ia, &iaro);
break;
case 1: /* iaro.u.ll.lh0x.MultiBlock_PktDlAss.* (IA_MultiBlock_PktDlAss_t) */
- /* TODO: Alloc DL TBF */
+ rc = gprs_rlcmac_handle_ccch_imm_ass_dl_tbf(ch_ts, ia, &iaro);
break;
}
/* TODO: iaro.u.lh.AdditionsR13.* (IA_AdditionsR13_t) */
@@ -205,7 +240,7 @@
}
break;
case 1: /* iaro.u.hh.u.UplinkDownlinkAssignment.ul_dl.Packet_Downlink_ImmAssignment* (Packet_Downlink_ImmAssignment_t) */
- /* TODO: Alloc DL TBF */
+ rc = gprs_rlcmac_handle_ccch_imm_ass_dl_tbf(ch_ts, ia, &iaro);
break;
}
break;
diff --git a/src/rlcmac/tbf_dl.c b/src/rlcmac/tbf_dl.c
new file mode 100644
index 0000000..82a3847
--- /dev/null
+++ b/src/rlcmac/tbf_dl.c
@@ -0,0 +1,85 @@
+/* Downlink TBF as per 3GPP TS 44.064 */
+/*
+ * (C) 2023 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+#include <osmocom/core/logging.h>
+
+#include <osmocom/gprs/rlcmac/tbf_dl.h>
+#include <osmocom/gprs/rlcmac/gre.h>
+
+struct gprs_rlcmac_dl_tbf *gprs_rlcmac_dl_tbf_alloc(struct gprs_rlcmac_entity *gre)
+{
+ struct gprs_rlcmac_dl_tbf *dl_tbf;
+ //int rc;
+
+ dl_tbf = talloc_zero(gre, struct gprs_rlcmac_dl_tbf);
+ if (!dl_tbf)
+ return NULL;
+
+ gprs_rlcmac_tbf_constructor(dl_tbf_as_tbf(dl_tbf), GPRS_RLCMAC_TBF_DIR_DL, gre);
+
+ //rc = gprs_rlcmac_tbf_dl_fsm_constructor(dl_tbf);
+ //if (rc < 0)
+ // goto err_tbf_destruct;
+
+ dl_tbf->tbf.nr = g_ctx->next_dl_tbf_nr++;
+
+ return dl_tbf;
+#if 0
+err_tbf_destruct:
+ gprs_rlcmac_tbf_destructor(dl_tbf_as_tbf(dl_tbf));
+ talloc_free(dl_tbf);
+ return NULL;
+#endif
+}
+
+void gprs_rlcmac_dl_tbf_free(struct gprs_rlcmac_dl_tbf *dl_tbf)
+{
+ if (!dl_tbf)
+ return;
+
+ //gprs_rlcmac_tbf_dl_fsm_destructor(dl_tbf);
+
+ gprs_rlcmac_tbf_destructor(dl_tbf_as_tbf(dl_tbf));
+ talloc_free(dl_tbf);
+}
+
+
+static uint8_t dl_tbf_dl_slotmask(struct gprs_rlcmac_dl_tbf *dl_tbf)
+{
+ uint8_t i;
+ uint8_t dl_slotmask = 0;
+
+ for (i = 0; i < 8; i++) {
+ if (dl_tbf->cur_alloc.ts[i].allocated)
+ dl_slotmask |= (1 << i);
+ }
+
+ return dl_slotmask;
+}
+
+int gprs_rlcmac_dl_tbf_configure_l1ctl(struct gprs_rlcmac_dl_tbf *dl_tbf)
+{
+ struct osmo_gprs_rlcmac_prim *rlcmac_prim;
+ uint8_t dl_slotmask = dl_tbf_dl_slotmask(dl_tbf);
+
+ LOGPTBFDL(dl_tbf, LOGL_INFO, "Send L1CTL-CF_DL_TBF.req dl_slotmask=0x%02x\n", dl_slotmask);
+ rlcmac_prim = gprs_rlcmac_prim_alloc_l1ctl_cfg_dl_tbf_req(dl_tbf->tbf.nr, dl_slotmask, dl_tbf->cur_alloc.dl_tfi);
+ return gprs_rlcmac_prim_call_down_cb(rlcmac_prim);
+}
diff --git a/tests/rlcmac/rlcmac_prim_test.c b/tests/rlcmac/rlcmac_prim_test.c
index 2070997..65c35de 100644
--- a/tests/rlcmac/rlcmac_prim_test.c
+++ b/tests/rlcmac/rlcmac_prim_test.c
@@ -119,6 +119,64 @@
0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b
};
+/*
+GSM CCCH - Immediate Assignment
+ L2 Pseudo Length
+ 0010 11.. = L2 Pseudo Length value: 11
+ .... 0110 = Protocol discriminator: Radio Resources Management messages (0x6)
+ .... 0110 = Protocol discriminator: Radio Resources Management messages (0x6)
+ 0000 .... = Skip Indicator: No indication of selected PLMN (0)
+ Message Type: Immediate Assignment
+ Page Mode
+ .... 0000 = Page Mode: Normal paging (0)
+ Dedicated mode or TBF
+ 0011 .... = Dedicated mode or TBF: This message assigns a downlink TBF to the mobile station identified in the IA Rest Octets IE (3)
+ Packet Channel Description
+ 0000 1... = Channel Type: 1
+ .... .111 = Timeslot: 7
+ 111. .... = Training Sequence: 7
+ .... .0.. = Spare: 0x00
+ .... ..11 0110 0111 = Single channel ARFCN: 871
+ Request Reference
+ Random Access Information (RA): 125
+ 1000 0... = T1': 16
+ .... .000 000. .... = T3: 0
+ ...0 0000 = T2: 0
+ [RFN: 21216]
+ Timing Advance
+ Timing advance value: 28
+ Mobile Allocation
+ Length: 0
+ IA Rest Octets
+ H... .... = First Discriminator Bit: High
+ .H.. .... = Second Discriminator Bit: High
+ ..0. .... = Discriminator Bit: Packet Assignment
+ ...1 .... = Discriminator Bit: Packet Downlink Assignment
+ Packet Downlink Assignment
+ .... 0000 0000 0000 0000 0000 0000 0000 0001 .... = TLLI: 0x00000001
+ .... 1... = TFI Assignment (etc): Present
+ .... .000 00.. .... = TFI_Assignment: 0
+ ..0. .... = RLC_Mode: RLC acknowledged mode
+ ...0 .... = Alpha: Not Present
+ .... 0000 0... .... = Gamma: 0 dB (0)
+ .0.. .... = Polling: no action is required from MS
+ ..0. .... = TA_Valid: the timing advance value is not valid
+ ...0 .... = Timing Advance Index: Not Present
+ .... 0... = TBF Starting Time: Not Present
+ .... .0.. = P0: Not Present
+ .... ..L. = Additions in R99: Not Present
+ .... ...L = Additions in Rel-6: Not Present
+ L... .... = Additions in Rel-7: Not Present
+ .L.. .... = Additions in Rel-10: Not Present
+ ..L. .... = Additions in Rel-13: Not Present
+ Padding Bits: default padding
+*/
+static uint8_t ccch_imm_ass_pkt_dl_tbf[] = {
+ 0x2d, 0x06, 0x3f, 0x30, 0x0f, 0xe3, 0x67, 0x7d, 0x80, 0x00,
+ 0x1c, 0x00, 0xd0, 0x00, 0x00, 0x00, 0x18, 0x00, 0x03,
+ 0x2b, 0x2b, 0x2b, 0x2b
+};
+
static int test_rlcmac_prim_up_cb(struct osmo_gprs_rlcmac_prim *rlcmac_prim, void *user_data)
{
const char *pdu_name = osmo_gprs_rlcmac_prim_name(rlcmac_prim);
@@ -163,6 +221,12 @@
rlcmac_prim->l1ctl.cfg_ul_tbf_req.ul_tbf_nr,
rlcmac_prim->l1ctl.cfg_ul_tbf_req.ul_slotmask);
break;
+ case OSMO_PRIM(OSMO_GPRS_RLCMAC_L1CTL_CFG_DL_TBF, PRIM_OP_REQUEST):
+ printf("%s(): Rx %s dl_tbf_nr=%u dl_slotmask=0x%02x dl_tfi=%u\n", __func__, pdu_name,
+ rlcmac_prim->l1ctl.cfg_dl_tbf_req.dl_tbf_nr,
+ rlcmac_prim->l1ctl.cfg_dl_tbf_req.dl_slotmask,
+ rlcmac_prim->l1ctl.cfg_dl_tbf_req.dl_tfi);
+ break;
default:
printf("%s(): Rx %s\n", __func__, pdu_name);
}
@@ -184,6 +248,12 @@
osmo_gprs_rlcmac_prim_set_down_cb(test_rlcmac_prim_down_cb, NULL);
}
+void cleanup_test(void)
+{
+ /* Reinit the RLCMAC layer so that data generated during the test is freed within the test context: */
+ osmo_gprs_rlcmac_init(OSMO_GPRS_RLCMAC_LOCATION_MS);
+}
+
static void test_ul_tbf_attach(void)
{
struct osmo_gprs_rlcmac_prim *rlcmac_prim;
@@ -211,7 +281,40 @@
rc = osmo_gprs_rlcmac_prim_lower_up(rlcmac_prim);
OSMO_ASSERT(rc == 0);
+
printf("=== %s end ===\n", __func__);
+ cleanup_test();
+}
+
+/* PCU allocates a DL TBF through PCH ImmAss for MS (when in packet-idle) */
+static void test_dl_tbf_ccch_assign(void)
+{
+ struct osmo_gprs_rlcmac_prim *rlcmac_prim;
+ int rc;
+
+ printf("=== %s start ===\n", __func__);
+ prepare_test();
+ uint32_t tlli = 0x0000001;
+ //uint8_t ts_nr = 7;
+ //uint8_t usf = 0;
+ //uint32_t rts_fn = 4;
+
+ /* Notify RLCMAC about our TLLI */
+ rlcmac_prim = osmo_gprs_rlcmac_prim_alloc_gmmrr_assign_req(tlli);
+ rc = osmo_gprs_rlcmac_prim_upper_down(rlcmac_prim);
+
+ OSMO_ASSERT(sizeof(ccch_imm_ass_pkt_dl_tbf) == GSM_MACBLOCK_LEN);
+ rlcmac_prim = osmo_gprs_rlcmac_prim_alloc_l1ctl_ccch_data_ind(0, ccch_imm_ass_pkt_dl_tbf);
+ rc = osmo_gprs_rlcmac_prim_lower_up(rlcmac_prim);
+ OSMO_ASSERT(rc == 0);
+
+ /* Trigger transmission of LLC data (GMM Attach) */
+ //rlcmac_prim = osmo_gprs_rlcmac_prim_alloc_l1ctl_pdch_rts_ind(ts_nr, rts_fn, usf);
+ //rc = osmo_gprs_rlcmac_prim_lower_up(rlcmac_prim);
+
+ OSMO_ASSERT(rc == 0);
+ printf("=== %s end ===\n", __func__);
+ cleanup_test();
}
static const struct log_info_cat test_log_categories[] = { };
@@ -235,6 +338,7 @@
log_set_use_color(osmo_stderr_target, 0);
test_ul_tbf_attach();
+ test_dl_tbf_ccch_assign();
talloc_free(tall_ctx);
}
diff --git a/tests/rlcmac/rlcmac_prim_test.err b/tests/rlcmac/rlcmac_prim_test.err
index e6d6b30..4658184 100644
--- a/tests/rlcmac/rlcmac_prim_test.err
+++ b/tests/rlcmac/rlcmac_prim_test.err
@@ -25,3 +25,10 @@
DLGLOBAL DEBUG TBF(UL:NR-0:TLLI-00002342) Copying 1 RLC blocks, 1 BSNs
DLGLOBAL DEBUG TBF(UL:NR-0:TLLI-00002342) Copying data unit 0 (BSN 0)
DLGLOBAL DEBUG TBF(UL:NR-0:TLLI-00002342) msg block (BSN 0, CS-2): 3c 00 01 01 c0 00 08 01 01 d5 71 00 00 08 29 26 24 00 00 00 00 71 62 f2 24 6c 84 44 04 11 e5 10 00 00
+DLGLOBAL INFO UL_TBF_ASS{IDLE}: Deallocated
+DLGLOBAL INFO UL_TBF{FLOW}: Deallocated
+DLGLOBAL INFO Rx from upper layers: GMMRR-ASSIGN.request
+DLGLOBAL INFO GMMRR-ASSIGN.req: creating new entity TLLI=0x00000001
+DLGLOBAL INFO Rx from lower layers: L1CTL-CCCH_DATA.indication
+DLGLOBAL INFO GRE(00000001) Got PCH IMM_ASS (DL_TBF): DL_TFI=0 TS=7
+DLGLOBAL INFO TBF(DL:NR-0:TLLI-00000001) Send L1CTL-CF_DL_TBF.req dl_slotmask=0x80
To view, visit change 31286. To unsubscribe, or for help writing mail filters, visit settings.