Commit 85e85fb9 authored by Cedric Roux's avatar Cedric Roux

- Update S1AP and SCTP layers to use ITTI for eNB: may broke the old version...

- Update S1AP and SCTP layers to use ITTI for eNB: may broke the old version of S1AP/SCTP (compile with USE_MME=R10 and ENABLE_ITTI=1)

pre-ci tests passed

git-svn-id: http://svn.eurecom.fr/openair4G/trunk@4342 818b1a75-f10b-46b9-bf7c-635c3b92a50f
parent d0de35dc
S1AP_DIR = $(OPENAIR3_DIR)/OPENAIRMME/S1AP
S1AP_OBJDIR = $(OPENAIR3_DIR)/OPENAIRMME/S1AP/enb_objs
include $(OPENAIR_TARGETS)/SIMU/USER/Makerules
S1AP_OBJDIR = $(S1AP_DIR)/enb_objs
ASN1MESSAGESDIR=$(S1AP_DIR)/MESSAGES
ASN1DIR=$(ASN1MESSAGESDIR)/ASN1
......@@ -26,7 +27,9 @@ libs1ap_OBJECTS = \
$(S1AP_OBJDIR)/s1ap_encoder.o \
$(S1AP_OBJDIR)/s1ap_decoder.o \
$(S1AP_OBJDIR)/s1ap_xer_print.o \
s1ap_eNB_decoder.o s1ap_eNB_encoder.o \
s1ap_eNB_itti_messaging.o \
s1ap_eNB_decoder.o \
s1ap_eNB_encoder.o \
s1ap_eNB_handlers.o \
s1ap_eNB_nnsf.o s1ap_eNB_ue_context.o \
s1ap_eNB_trace.o s1ap_eNB_overload.o \
......@@ -35,42 +38,30 @@ libs1ap_OBJECTS = \
$(addprefix MESSAGES/, $(S1AP_ASN_MODULE_SOURCES))
# pull in dependency info for *existing* .o files
-include .*.d
# -include .*.d
-include *.d
CFLAGS = \
CFLAGS = \
-DENB_MODE \
-DENABLE_USE_MME \
-DEMIT_ASN_DEBUG=1 \
-DUSER_MODE \
-I./MESSAGES \
-I$(S1AP_OBJDIR) \
-I../GTPV1-U \
-I../GTPV1-U/nw-gtpv1u/shared \
-I../GTPV1-U/nw-gtpv1u/include \
-I../SCTP \
-I../UDP \
-I../UTILS \
-I../UTILS/HASHTABLE \
-I$(OPENAIR2_DIR)/COMMON \
-I$(S1AP_DIR) \
-I$(OPENAIR2_DIR) \
-I$(OPENAIR2_DIR)/UTIL \
-I../UTILS \
$(ADD_CFLAGS) \
-DENB_MODE \
-DENABLE_USE_MME \
-DEMIT_ASN_DEBUG_EXTERN \
-DUSER_MODE \
-g \
-O2 \
-Wall \
$(S1AP_CFLAGS) \
-Werror=implicit-function-declaration
$(libs1ap_OBJECTS): %.o : %.c
@echo "Compiling $<"
@$(CC) -c $(CFLAGS) -o $@ $<
@$(CC) -MM $(CFLAGS) $*.c > .$(notdir $*).d
@mv -f .$(notdir $*).d .$(notdir $*).d.tmp
@sed -e 's|.*:|$*.o:|' < .$(notdir $*).d.tmp > .$(notdir $*).d
@sed -e 's/.*://' -e 's/\\$$//' < .$(notdir $*).d.tmp | fmt -1 | \
sed -e 's/^ *//' -e 's/$$/:/' >> .$(notdir $*).d
@rm -f .$(notdir $*).tmp
@$(CC) -MM $(CFLAGS) $*.c > $*.d
@mv -f $*.d $*.d.tmp
@sed -e 's|.*:|$*.o:|' < $*.d.tmp > $*.d
@sed -e 's/.*://' -e 's/\\$$//' < $*.d.tmp | fmt -1 | \
sed -e 's/^ *//' -e 's/$$/:/' >> $*.d
@rm -f $*.d.tmp
$(S1AP_OBJDIR)/s1ap_ieregen.stamp: $(ASN1DIR)/$(ASN1RELDIR)/S1AP-PDU-Contents.asn $(ASN1DIR)/asn1tostruct.py
mkdir -p $(S1AP_OBJDIR)
......@@ -87,10 +78,19 @@ libs1ap.a: $(S1AP_OBJDIR)/s1ap_ieregen.stamp $(S1AP_OBJDIR)/s1ap_asn1regen.stamp
@$(AR) rcs $@ $(libs1ap_OBJECTS)
clean:
rm -f $(libs1ap_OBJECTS)
rm -f $(libs1ap_OBJECTS:.o=.d)
rm -f libs1ap.a
rm -f $(S1AP_OBJDIR)/s1ap_asn1regen.stamp
rm -f $(S1AP_OBJDIR)/s1ap_ieregen.stamp
rm -f $(S1AP_OBJDIR)/s1ap_decoder.c $(S1AP_OBJDIR)/s1ap_encoder.c
rm -f $(S1AP_OBJDIR)/s1ap_xer_print.c $(S1AP_OBJDIR)/s1ap_ies_defs.h
\ No newline at end of file
@$(RM_F_V) $(libs1ap_OBJECTS)
@$(RM_F_V) .*.d
@$(RM_F_V) $(addprefix MESSAGES/, $(S1AP_ASN_MODULE_SOURCES))
@$(RM_F_V) $(addprefix MESSAGES/, $(S1AP_ASN_MODULE_SOURCES:.o=.d))
@$(RM_F_V) libs1ap.a
@$(RM_F_V) $(S1AP_OBJDIR)/s1ap_asn1regen.stamp
@$(RM_F_V) $(S1AP_OBJDIR)/s1ap_ieregen.stamp
@$(RM_F_V) $(S1AP_OBJDIR)/s1ap_decoder.c $(S1AP_OBJDIR)/s1ap_encoder.c
@$(RM_F_V) $(S1AP_OBJDIR)/s1ap_xer_print.c $(S1AP_OBJDIR)/s1ap_ies_defs.h
cleanall: clean
@$(RM_F_V) $(addprefix MESSAGES/, $(S1AP_ASN_MODULE_SOURCES:.o=.c))
@$(RM_F_V) $(addprefix MESSAGES/, $(S1AP_ASN_MODULE_SOURCES:.o=.h))
showcflags:
@echo s1ap cflags: $(CFLAGS)
\ No newline at end of file
......@@ -41,8 +41,9 @@
#include "S1AP-PDU.h"
int asn_debug = 0;
int asn1_xer_print = 0;
int asn1_xer_print = 1;
#if defined(EMIT_ASN_DEBUG_EXTERN)
inline void ASN_DEBUG(const char *fmt, ...)
{
if (asn_debug) {
......@@ -56,6 +57,7 @@ inline void ASN_DEBUG(const char *fmt, ...)
va_end(ap);
}
}
#endif
ssize_t s1ap_generate_initiating_message(
uint8_t **buffer,
......
......@@ -44,7 +44,9 @@
// extern int asn_debug_indent;
extern int asn_debug;
#if defined(EMIT_ASN_DEBUG_EXTERN)
inline void ASN_DEBUG(const char *fmt, ...);
#endif
#include "Criticality.h"
#include "Presence.h"
......@@ -400,9 +402,8 @@ extern int asn_debug;
extern int asn1_xer_print;
#if defined(ENB_MODE)
# include "mme_sim.h"
# include "UTIL/LOG/log.h"
# include "eNB_default_values.h"
# include "s1ap_eNB_default_values.h"
# define S1AP_ERROR(x, args...) LOG_E(S1AP, x, ##args)
# define S1AP_WARN(x, args...) LOG_W(S1AP, x, ##args)
# define S1AP_TRAF(x, args...) LOG_T(S1AP, x, ##args)
......@@ -420,19 +421,18 @@ struct s1ap_message_s;
/** \brief Function callback prototype.
**/
#if defined(ENB_MODE)
typedef int (*s1ap_message_decoded_callback)(
eNB_mme_desc_t *eNB_desc_p,
sctp_queue_item_t *packet_p,
struct s1ap_message_s *message_p
);
#else
// typedef int (*s1ap_message_decoded_callback)(
// eNB_mme_desc_t *eNB_desc_p,
// sctp_queue_item_t *packet_p,
// struct s1ap_message_s *message_p
// );
typedef int (*s1ap_message_decoded_callback)(
uint32_t assocId,
uint32_t stream,
uint32_t assoc_id,
uint32_t stream,
struct s1ap_message_s *message_p
);
#endif
/** \brief Encode a successfull outcome message
\param buffer pointer to buffer in which data will be encoded
\param length pointer to the length of buffer
......
This diff is collapsed.
......@@ -31,101 +31,18 @@
#include <stdio.h>
#include <stdint.h>
#include "tree.h"
#include "queue.h"
#include "mme_sim.h"
#include "s1ap_eNB_defs.h"
/** @defgroup _s1ap_impl_ S1AP Layer Reference Implementation for eNB
* @ingroup _ref_implementation_
* @{
*/
#ifndef S1AP_ENB_H_
#define S1AP_ENB_H_
/* Served PLMN identity element */
struct plmn_identity_s {
uint16_t mcc;
uint16_t mnc;
STAILQ_ENTRY(plmn_identity_s) next;
};
/* Served group id element */
struct served_group_id_s {
uint16_t mme_group_id;
STAILQ_ENTRY(served_group_id_s) next;
};
/* Served mme code for a particular MME */
struct mme_code_s {
uint8_t mme_code;
STAILQ_ENTRY(mme_code_s) next;
};
/* Served gummei element */
struct served_gummei_s {
/* Number of MME served PLMNs */
uint8_t nb_served_plmns;
/* List of served PLMNs by MME */
STAILQ_HEAD(served_plmns_s, plmn_identity_s) served_plmns;
/* Number of group id in list */
uint8_t nb_group_id;
/* Served group id list */
STAILQ_HEAD(served_group_ids_s, served_group_id_s) served_group_ids;
/* Number of MME code */
uint8_t nb_mme_code;
/* MME Code to uniquely identify an MME within an MME pool area */
STAILQ_HEAD(mme_codes_s, mme_code_s) mme_codes;
/* Next GUMMEI element */
STAILQ_ENTRY(served_gummei_s) next;
};
/* This structure describes association of a eNB to a MME */
typedef struct s1ap_eNB_mme_data_s {
/* This is the optional name provided by the MME */
char *mme_name;
/* Remote MME IP addr */
char *ip_addr;
/* List of served GUMMEI per MME. There is one GUMMEI per RAT with a max
* number of 8 RATs but in our case only one is used. The LTE related pool
* configuration is included on the first place in the list.
*/
STAILQ_HEAD(served_gummeis_s, served_gummei_s) served_gummei;
/* Relative processing capacity of an MME with respect to the other MMEs
* in the pool in order to load-balance MMEs within a pool as defined
* in TS 23.401.
*/
uint8_t relative_mme_capacity;
/* Current MME overload information (if any). */
s1ap_overload_state_t overload_state;
/* Current eNB->MME S1AP association state */
s1ap_eNB_state_t state;
/* SCTP related data for this MME */
sctp_data_t sctp_data;
/* Next usable stream for UE signalling */
int32_t nextstream;
/* MME descriptors tree, ordered by sctp assoc id */
RB_ENTRY(s1ap_eNB_mme_data_s) entry;
} s1ap_eNB_mme_data_t;
inline int s1ap_eNB_compare_assoc_id(
struct s1ap_eNB_mme_data_s *p1, struct s1ap_eNB_mme_data_s *p2);
/* Generate the tree management functions */
RB_PROTOTYPE(s1ap_mme_map, s1ap_eNB_mme_data_s, entry,
s1ap_eNB_compare_assoc_id);
struct s1ap_eNB_mme_data_s *s1ap_eNB_get_MME(eNB_mme_desc_t *eNB_desc_p,
uint32_t assocId);
int s1ap_eNB_init(eNB_mme_desc_t *eNB_desc_p,
char *local_ip_addr[], int nb_local_ip,
char *remote_ip_addr[], int nb_remote_ip);
void *s1ap_eNB_task(void *arg);
#endif /* S1AP_ENB_H_ */
/**
* @}
*/
......@@ -106,7 +106,8 @@ static int s1ap_eNB_decode_unsuccessful_outcome(s1ap_message *message,
return -1;
}
int s1ap_eNB_decode_pdu(s1ap_message *message, uint8_t *buffer, uint32_t length)
int s1ap_eNB_decode_pdu(s1ap_message *message, const uint8_t * const buffer,
const uint32_t length)
{
S1AP_PDU_t pdu;
S1AP_PDU_t *pdu_p = &pdu;
......
......@@ -34,7 +34,7 @@
#ifndef S1AP_ENB_DECODER_H_
#define S1AP_ENB_DECODER_H_
int s1ap_eNB_decode_pdu(s1ap_message *message, uint8_t *buffer, uint32_t length)
__attribute__ ((warn_unused_result));
int s1ap_eNB_decode_pdu(s1ap_message *message, const uint8_t * const buffer,
const uint32_t length) __attribute__ ((warn_unused_result));
#endif /* S1AP_ENB_DECODER_H_ */
#ifndef S1AP_ENB_DEFAULT_VALUES_H_
#define S1AP_ENB_DEFAULT_VALUES_H_
#define ENB_TAC (0)
#define ENB_MCC (208)
#define ENB_MNC (34)
#define ENB_NAME "Eurecom ENB"
#define ENB_NAME_FORMAT (ENB_NAME" %u")
#define S1AP_PORT_NUMBER (36412)
#define S1AP_SCTP_PPID (18)
#define X2AP_PORT_NUMBER (36422)
#define X2AP_SCTP_PPID (27)
#define SCTP_OUT_STREAMS (64)
#define SCTP_IN_STREAMS (64)
#define SCTP_MAX_ATTEMPTS (5)
#define SCTP_RECV_BUFFER_SIZE (1024)
#endif /* S1AP_ENB_DEFAULT_VALUES_H_ */
......@@ -82,12 +82,12 @@ typedef enum {
S1AP_OVERLOAD_MAX,
} s1ap_overload_state_t;
typedef enum {
PAGING_DRX_32 = 0x0,
PAGING_DRX_64 = 0x1,
PAGING_DRX_128 = 0x2,
PAGING_DRX_256 = 0x3,
} paging_drx_t;
// typedef enum {
// PAGING_DRX_32 = 0x0,
// PAGING_DRX_64 = 0x1,
// PAGING_DRX_128 = 0x2,
// PAGING_DRX_256 = 0x3,
// } paging_drx_t;
typedef struct {
/* Octet string data */
......@@ -176,161 +176,156 @@ typedef struct {
} identity;
} ue_identity_t;
typedef struct {
/* The NAS First Req is the first message exchanged between RRC and S1AP
* for an UE.
* The rnti uniquely identifies an UE within a cell. Later the enb_ue_s1ap_id
* will be the unique identifier used between RRC and S1AP.
/* Served PLMN identity element */
struct plmn_identity_s {
uint16_t mcc;
uint16_t mnc;
STAILQ_ENTRY(plmn_identity_s) next;
};
/* Served group id element */
struct served_group_id_s {
uint16_t mme_group_id;
STAILQ_ENTRY(served_group_id_s) next;
};
/* Served mme code for a particular MME */
struct mme_code_s {
uint8_t mme_code;
STAILQ_ENTRY(mme_code_s) next;
};
/* Served gummei element */
struct served_gummei_s {
/* Number of MME served PLMNs */
uint8_t nb_served_plmns;
/* List of served PLMNs by MME */
STAILQ_HEAD(served_plmns_s, plmn_identity_s) served_plmns;
/* Number of group id in list */
uint8_t nb_group_id;
/* Served group id list */
STAILQ_HEAD(served_group_ids_s, served_group_id_s) served_group_ids;
/* Number of MME code */
uint8_t nb_mme_code;
/* MME Code to uniquely identify an MME within an MME pool area */
STAILQ_HEAD(mme_codes_s, mme_code_s) mme_codes;
/* Next GUMMEI element */
STAILQ_ENTRY(served_gummei_s) next;
};
struct s1ap_eNB_instance_s;
/* This structure describes association of a eNB to a MME */
typedef struct s1ap_eNB_mme_data_s {
/* MME descriptors tree, ordered by sctp assoc id */
RB_ENTRY(s1ap_eNB_mme_data_s) entry;
/* This is the optional name provided by the MME */
char *mme_name;
/* List of served GUMMEI per MME. There is one GUMMEI per RAT with a max
* number of 8 RATs but in our case only one is used. The LTE related pool
* configuration is included on the first place in the list.
*/
uint16_t rnti;
STAILQ_HEAD(served_gummeis_s, served_gummei_s) served_gummei;
rrc_establishment_cause_t establishment_cause;
nas_pdu_t nas_pdu;
/* If this flag is set S1AP layer is expecting the GUMMEI. If = 0,
* the temporary s-tmsi is used.
/* Relative processing capacity of an MME with respect to the other MMEs
* in the pool in order to load-balance MMEs within a pool as defined
* in TS 23.401.
*/
ue_identity_t ue_identity;
} s1ap_nas_first_req_t;
uint8_t relative_mme_capacity;
typedef struct {
unsigned eNB_ue_s1ap_id:24;
nas_pdu_t nas_pdu;
} s1ap_nas_uplink_t;
/* Current MME overload information (if any). */
s1ap_overload_state_t overload_state;
/* Current eNB->MME S1AP association state */
s1ap_eNB_state_t state;
typedef struct {
nas_pdu_t nas_pdu;
// cause_t cause;
} s1ap_nas_non_delivery_t;
/* Next usable stream for UE signalling */
int32_t nextstream;
typedef struct {
uint16_t rnti;
unsigned eNB_ue_s1ap_id:24;
/* Number of input/ouput streams */
uint16_t in_streams;
uint16_t out_streams;
/* Number of e_rab to be setup in the list */
uint8_t nb_of_e_rabs;
/* list of e_rab to be setup by RRC layers */
e_rab_t **e_rab_param;
} s1ap_e_rab_setup_req_t,
s1ap_initial_ctxt_setup_req_t;
/* Connexion id used when SCTP association is not established yet */
uint16_t cnx_id;
typedef struct {
unsigned eNB_ue_s1ap_id:24;
ue_radio_cap_t ue_radio_cap;
} s1ap_ue_cap_info_ind_t;
/* SCTP association id */
int32_t assoc_id;
typedef struct {
unsigned eNB_ue_s1ap_id:24;
/* Number of e_rab setup-ed in the list */
uint8_t nb_of_e_rabs;
/* list of e_rab setup-ed by RRC layers */
e_rab_setup_t *e_rabs;
/* Number of e_rab failed to be setup in list */
uint8_t nb_of_e_rabs_failed;
/* list of e_rabs that failed to be setup */
e_rab_failed_t *e_rabs_failed;
} s1ap_initial_ctxt_setup_resp_t;
/* Only meaningfull in virtual mode */
struct s1ap_eNB_instance_s *s1ap_eNB_instance;
} s1ap_eNB_mme_data_t;
typedef enum {
/** RRC -> S1AP API **/
#define S1AP_API_REQ_IS_INPUT(x) (x & 0x10)
/* RRC should use this api request for the first NAS message */
S1AP_API_NAS_FIRST_REQ = 0x10,
/* NAS messages with no context activation */
S1AP_API_NAS_UPLINK = 0x11,
/* When the eNB decides to not start the delivery of a NAS message that has
* been received over a UE-associated logical S1-connection or the eNB is
* unable to ensure that the message has been received by the UE, it shall
* report the non-delivery of this NAS message.
*/
S1AP_API_NAS_NON_DELIVERY_IND = 0x12,
/* This message is sent by the eNB to request the MME to switch DL GTP
* tunnel termination point(s) from one end-point to another.
*/
S1AP_API_PATH_SWITCH_REQ = 0x13,
/* This message is used to report the outcome of the request from the
* S1AP_API_NAS_INITIAL_CONTEXT_SETUP_REQ API request.
*/
S1AP_API_INITIAL_CONTEXT_SETUP_RESP = 0x14,
/* This message is used to report the unsuccessfull outcome of the request
* from the S1AP_API_NAS_INITIAL_CONTEXT_SETUP_REQ API request.
*/
S1AP_API_INITIAL_CONTEXT_SETUP_FAIL = 0x15,
/* This message is used to report the outcome of the request from the
* S1AP_API_INITIAL_CONTEXT_SETUP_REQ API request.
*/
S1AP_API_E_RAB_SETUP_RESP = 0x16,
/* This message is used to report the outcome of the request from the
* S1AP_API_E_RAB_SETUP_REQ API request.
*/
S1AP_API_E_RAB_MODIFY_RESP = 0x17,
/* This message is used to report the outcome of the request from the
* S1AP_API_E_RAB_MODIFY_REQ API request.
*/
S1AP_API_E_RAB_RELEASE_RESP = 0x18,
/* The purpose of the UE Capability Info Indication procedure is to enable
* the eNB to provide to the MME UE capability-related information.
*/
S1AP_API_UE_CAP_INFO_IND = 0x19,
/** S1AP -> RRC API **/
#define S1AP_API_REQ_IS_OUTPUT(x) (x & 0x20)
/* S1AP layer received a valid downlink info transfer message */
S1AP_API_NAS_DOWNLINK = 0x20,
/* The purpose of the Initial Context Setup procedure is to establish the
* necessary overall initial UE Context including ERAB context, the Security
* Key, Handover Restriction List, UE Radio capability and UE Security
* Capabilities etc.
typedef struct s1ap_eNB_instance_s {
/* Next s1ap eNB association.
* Only used for virtual mode.
*/
S1AP_API_NAS_INITIAL_CONTEXT_SETUP_REQ = 0x21,
/* The purpose of the E-RAB Setup procedure is to assign resources on Uu and
* S1 for one or several E-RABs and to setup corresponding Data Radio
* Bearers for a given UE.
*/
S1AP_API_E_RAB_SETUP_REQ = 0x22,
/* This message is sent by the MME and is used to request the eNB to modify
* the Data Radio Bearers and the allocated resources on Uu and S1 for one
* or several E-RABs.
*/
S1AP_API_E_RAB_MODIFY_REQ = 0x23,
/* This message is sent by the MME and is used to request the eNB to release
* allocated resources on Uu and S1 for one or several E-RABs.
STAILQ_ENTRY(s1ap_eNB_instance_s) s1ap_eNB_entries;
/* Tree of S1AP MME associations ordered by association ID */
RB_HEAD(s1ap_mme_map, s1ap_eNB_mme_data_s) s1ap_mme_head;
/* TODO: add a map ordered by relative MME capacity */
/* Tree of UE ordered by eNB_ue_s1ap_id's */
RB_HEAD(s1ap_ue_map, s1ap_eNB_ue_context_s) s1ap_ue_head;
/* For virtual mode, mod_id as defined in the rest of the L1/L2 stack */
uint8_t mod_id;
/* Displayable name of eNB */
char *eNB_name;
/* Unique eNB_id to identify the eNB within EPC.
* In our case the eNB is a macro eNB so the id will be 20 bits long.
* For Home eNB id, this field should be 28 bits long.
*/
S1AP_API_E_RAB_RELEASE_REQ = 0x24,
/** S1AP <-> RRC API **/
/** Messages below are bi-directionnal and can be triggered by both RRC and
* S1AP.
**/
#define S1AP_API_REQ_IS_BIDIR(x) (x & 0x40)
/* At reception of RESET message, the eNB (or MME) shall release all
* allocated resources on S1 and Uu related to the UE association(s)
* indicated explicitly or implicitly in the RESET message and remove the
* indicated UE contexts including S1AP ID.
uint32_t eNB_id;
/* The type of the cell */
enum cell_type_e cell_type;
/* Tracking area code */
uint16_t tac;
/* Mobile Country Code
* Mobile Network Code
*/
S1AP_API_RESET = 0x40,
S1AP_API_RESET_ACK = 0x41,
} s1ap_rrc_api_req_type_t;
uint16_t mcc;
uint16_t mnc;
/* Default Paging DRX of the eNB as defined in TS 36.304 */
paging_drx_t default_drx;
} s1ap_eNB_instance_t;
typedef struct {
/* The API request type */
s1ap_rrc_api_req_type_t api_req;
union {
/** RRC -> S1AP requests **/
s1ap_nas_first_req_t first_nas_req;
s1ap_nas_uplink_t nas_uplink;
s1ap_initial_ctxt_setup_resp_t initial_ctxt_resp;
s1ap_nas_non_delivery_t nas_non_delivery;
s1ap_ue_cap_info_ind_t ue_cap_info_ind;
/** S1AP -> RRC requests **/
s1ap_e_rab_setup_req_t e_rab_setup_req;
s1ap_initial_ctxt_setup_req_t initial_ctxt_setup_req;
} msg;
} s1ap_rrc_api_req_t;
/* Callback notifier.