aboutsummaryrefslogtreecommitdiffstats
path: root/stacks/lwip_stack/lwip_src/include/netif
diff options
context:
space:
mode:
Diffstat (limited to 'stacks/lwip_stack/lwip_src/include/netif')
-rw-r--r--stacks/lwip_stack/lwip_src/include/netif/common.h233
-rw-r--r--stacks/lwip_stack/lwip_src/include/netif/kni_proc.h103
-rw-r--r--stacks/lwip_stack/lwip_src/include/netif/sc_dpdk.h80
-rw-r--r--stacks/lwip_stack/lwip_src/include/netif/sharedmemory.h148
-rw-r--r--stacks/lwip_stack/lwip_src/include/netif/spl_hal.h114
5 files changed, 678 insertions, 0 deletions
diff --git a/stacks/lwip_stack/lwip_src/include/netif/common.h b/stacks/lwip_stack/lwip_src/include/netif/common.h
new file mode 100644
index 0000000..45e9c8f
--- /dev/null
+++ b/stacks/lwip_stack/lwip_src/include/netif/common.h
@@ -0,0 +1,233 @@
+/*
+*
+* Copyright (c) 2018 Huawei Technologies Co.,Ltd.
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at:
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+#ifndef _COMMON_H_
+#define _COMMON_H_
+#include <stddef.h> /* for size_t */
+#include "stackx_common.h"
+#include "netif.h"
+
+#ifdef __cplusplus
+/* *INDENT-OFF* */
+extern "C" {
+/* *INDENT-ON* */
+#endif
+
+//yyq
+
+/* define common names for structures shared between server and client */
+
+#define MP_STACKX_INSTANCE_POLL_NAME "VppTCP_instance_poll"
+#define MP_STACKX_PORT_ZONE "VppTCP_stackx_port_zone"
+#define MP_STACKX_PORT_INFO "VppTCP_stackx_port_info"
+#define MP_MEMPOLL_RX_NAME "VppTCP_MBUF_%u_%u_RX"
+#define MP_MEMPOLL_RXMSG_NAME "VppTCP_MSG_%u_%u_RX"
+#define MP_MEMPOLL_TX_NAME "VppTCP_MBUF_TX"
+#define MP_MEMPOLL_SEG_NAME "VppTCP_MBUF_SEG"
+#define MP_STACKX_MSG_NAME "VppTCP_msg"
+#define MP_STACKX_RING_NAME "VppTCP_%u_ring"
+#define MP_STACKX_LRING_NAME "VppTCP_%u_lring"
+
+#define MP_STACKX_BIT_SET_NAME "%s_bit_set"
+
+#define MP_STACKX_SOCKET_FREE_LIST_NAME "VppTCP_socket_list"
+
+#define MP_NETIF_LIST_NAME "VppTCP_Netif_list"
+
+/*
+ move sharemem create from App to nstackMain
+advice rename app_tx_mbuf to VppTCP_APP_TXBUF_POOL
+*/
+#define MP_STACKX_APP_TXBUF_POOL "app_tx_mbuf"
+
+#define MP_MEMPOLL_TCP_BUFF_NAME "VppTCP_MBUF_%u_TCP_BUFF"
+
+extern int spl_snprintf (char *buffer, int buflen, const char *format, ...);
+
+/*According to the number of network cards, the establishment of recv lring,
+ *each separate, each proc_id each NIC queue independent lring
+ */
+static inline const char *
+get_mempoll_rx_name (unsigned proc_id, unsigned nic_id)
+{
+ /* buffer for return value. Size calculated by %u being replaced
+ * by maximum 3 digits (plus an extra byte for safety)
+ * the id may reach 65535, need add more space*/
+ static char buffer[sizeof (MP_MEMPOLL_RX_NAME) + 32];
+
+ int retVal = spl_snprintf (buffer, sizeof (buffer) - 1, MP_MEMPOLL_RX_NAME, proc_id, nic_id); //???????????buffer??
+ if (-1 == retVal)
+ {
+ NSPOL_LOGERR ("spl_snprintf failed]");
+ return NULL;
+ }
+
+ return buffer;
+}
+
+static inline const char *
+get_mempoll_rxmsg_name (unsigned proc_id, unsigned nic_id)
+{
+ /* buffer for return value. Size calculated by %u being replaced
+ * by maximum 3 digits (plus an extra byte for safety)
+ * the id may reach 65535, need add more space*/
+ static char buffer[sizeof (MP_MEMPOLL_RXMSG_NAME) + 32];
+
+ int retVal = spl_snprintf (buffer, sizeof (buffer) - 1, MP_MEMPOLL_RXMSG_NAME, proc_id, nic_id); //???????????buffer??
+ if (-1 == retVal)
+ {
+ NSPOL_LOGERR ("spl_snprintf failed]");
+ return NULL;
+ }
+
+ return buffer;
+}
+
+static inline const char *
+get_mempoll_ring_name (unsigned proc_id)
+{
+ static char buffer[sizeof (MP_STACKX_RING_NAME) + 16];
+
+ int retVal =
+ spl_snprintf (buffer, sizeof (buffer) - 1, MP_STACKX_RING_NAME, proc_id);
+ if (-1 == retVal)
+ {
+ NSPOL_LOGERR ("spl_snprintf failed]");
+ return NULL;
+ }
+
+ return buffer;
+}
+
+static inline const char *
+get_mempoll_lring_name (unsigned proc_id)
+{
+ static char buffer[sizeof (MP_STACKX_LRING_NAME) + 16];
+
+ int retVal =
+ spl_snprintf (buffer, sizeof (buffer) - 1, MP_STACKX_LRING_NAME, proc_id);
+ if (-1 == retVal)
+ {
+ NSPOL_LOGERR ("spl_snprintf failed]");
+ return NULL;
+ }
+
+ return buffer;
+}
+
+static inline const char *
+get_mempoll_msg_name ()
+{
+ static char buffer[sizeof (MP_STACKX_MSG_NAME) + 16];
+
+ int retVal = spl_snprintf (buffer, sizeof (buffer) - 1, MP_STACKX_MSG_NAME);
+ if (-1 == retVal)
+ {
+ NSPOL_LOGERR ("spl_snprintf failed]");
+ return NULL;
+ }
+
+ return buffer;
+}
+
+static inline const char *
+get_mempoll_tx_name ()
+{
+ /* buffer for return value. Size calculated by %u being replaced
+ * by maximum 3 digits (plus an extra byte for safety) */
+ static char buffer[sizeof (MP_MEMPOLL_TX_NAME) + 16];
+
+ int retVal = spl_snprintf (buffer, sizeof (buffer) - 1, MP_MEMPOLL_TX_NAME); //???????????buffer??
+ if (-1 == retVal)
+ {
+ NSPOL_LOGERR ("spl_snprintf failed]");
+ return NULL;
+ }
+
+ return buffer;
+}
+
+static inline const char *
+get_mempoll_seg_name ()
+{
+ /* buffer for return value. Size calculated by %u being replaced
+ * by maximum 3 digits (plus an extra byte for safety) */
+ static char buffer[sizeof (MP_MEMPOLL_SEG_NAME) + 16];
+
+ int retVal = spl_snprintf (buffer, sizeof (buffer) - 1, MP_MEMPOLL_SEG_NAME); //???????????buffer??
+ if (-1 == retVal)
+ {
+ NSPOL_LOGERR ("spl_snprintf failed]");
+ return NULL;
+ }
+
+ return buffer;
+}
+
+static inline const char *
+get_memStatusBitSet_name (const char *name, unsigned int num)
+{
+ static char buffer[64];
+
+ int retVal =
+ spl_snprintf (buffer, sizeof (buffer) - 1, MP_STACKX_BIT_SET_NAME "%d",
+ name, num);
+ if (-1 == retVal)
+ {
+ NSPOL_LOGERR ("spl_snprintf failed]");
+ return NULL;
+ }
+
+ return buffer;
+}
+
+static inline const char *
+get_memNameWithProc (const char *name)
+{
+ static char buffer[64];
+
+ int retVal = spl_snprintf (buffer, sizeof (buffer) - 1, "%s", name);
+ if (-1 == retVal)
+ {
+ NSPOL_LOGERR ("spl_snprintf failed]");
+ return NULL;
+ }
+
+ return buffer;
+}
+
+static inline const char *
+get_memInstancePoll_name (unsigned int type)
+{
+ static char buffer[sizeof (MP_STACKX_INSTANCE_POLL_NAME) + 32];
+ int retVal = spl_snprintf (buffer, sizeof (buffer) - 1, "%s" "%d",
+ MP_STACKX_INSTANCE_POLL_NAME, type);
+ if (-1 == retVal)
+ {
+ NSPOL_LOGERR ("spl_snprintf failed]");
+ return NULL;
+ }
+
+ return buffer;
+}
+
+#ifdef __cplusplus
+/* *INDENT-OFF* */
+}
+/* *INDENT-ON* */
+#endif
+
+#endif
diff --git a/stacks/lwip_stack/lwip_src/include/netif/kni_proc.h b/stacks/lwip_stack/lwip_src/include/netif/kni_proc.h
new file mode 100644
index 0000000..bcf4129
--- /dev/null
+++ b/stacks/lwip_stack/lwip_src/include/netif/kni_proc.h
@@ -0,0 +1,103 @@
+/*
+*
+* Copyright (c) 2018 Huawei Technologies Co.,Ltd.
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at:
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+#ifndef _KNI_PROC_H_
+#define _KNI_PROC_H_
+#include <rte_kni.h>
+#include <rte_mempool.h>
+#include <rte_mbuf.h>
+
+#ifdef __cplusplus
+/* *INDENT-OFF* */
+extern "C" {
+/* *INDENT-ON* */
+#endif
+
+#define KNI_MAX_KTHREAD 32
+
+/* the kni is runned on the lcore from below on */
+#define DEFAULT_KNI_LCORE_BASE 24
+
+/* Structure of kni para */
+struct disp_kni_para
+{
+ u8_t queue_id;
+ u8_t port_id;
+};
+
+/*
+ * Structure of port parameters
+ */
+struct kni_port_params
+{
+ uint8_t port_id; /* Port ID */
+ unsigned lcore_rx; /* lcore ID for RX */
+ unsigned lcore_tx; /* lcore ID for TX */
+ uint32_t nb_lcore_k; /* Number of lcores for KNI multi kernel threads */
+ uint32_t nb_kni; /* Number of KNI devices to be created */
+ unsigned lcore_k[KNI_MAX_KTHREAD]; /* lcore ID list for kthreads */
+ struct rte_kni *kni[KNI_MAX_KTHREAD]; /* KNI context pointers */
+ struct rte_mempool *kni_pktmbuf_pool;
+ u8_t ip_reconfigured;
+} __rte_cache_aligned;
+
+/* Structure type for storing kni interface specific stats */
+struct kni_interface_stats
+{
+ /* number of pkts received from NIC, and sent to KNI */
+ uint64_t rx_packets;
+
+ /* number of pkts received from NIC, but failed to send to KNI */
+ uint64_t rx_dropped;
+
+ /* number of pkts received from KNI, and sent to NIC */
+ uint64_t tx_packets;
+
+ /* number of pkts received from KNI, but failed to send to NIC */
+ uint64_t tx_dropped;
+};
+
+/* External interface for initilizing KNI subsystem */
+int kni_proc_init (enum rte_proc_type_t proc_type, int proc_id, u32_t pmask,
+ struct kni_port_params **kni_para);
+
+int kni_proc_init_secondary (int proc_id, int port_id);
+/* External interface for destroy KNI subsystem */
+void kni_proc_free (void);
+
+/* External interface for kni tx thread entry */
+void kni_tx_thread_cycle (void *arg);
+
+int kni_config_net (void);
+void kni_handler_eth_operate_request (int port);
+
+/* External interface for commiting packet to kni device */
+void kni_dispatch_to_kernel (uint8_t port_id,
+ struct rte_mbuf *pkts_burst[], uint8_t nb_rx);
+
+/* the lcore kni tx/rx thread run on */
+unsigned kni_get_tx_lcore (uint8_t port_id);
+unsigned kni_get_rx_lcore (uint8_t port_id);
+
+void init_kni (void);
+
+#ifdef __cplusplus
+/* *INDENT-OFF* */
+}
+/* *INDENT-ON* */
+#endif
+
+#endif /*#ifndef _KNI_PROC_H_ */
diff --git a/stacks/lwip_stack/lwip_src/include/netif/sc_dpdk.h b/stacks/lwip_stack/lwip_src/include/netif/sc_dpdk.h
new file mode 100644
index 0000000..99c3bc6
--- /dev/null
+++ b/stacks/lwip_stack/lwip_src/include/netif/sc_dpdk.h
@@ -0,0 +1,80 @@
+/*
+*
+* Copyright (c) 2018 Huawei Technologies Co.,Ltd.
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at:
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+#ifndef SC_DPDK_H_
+#define SC_DPDK_H_
+
+#include <pbuf.h>
+#include <sharedmemory.h>
+
+#include "lwip/etharp.h"
+
+#ifdef __cplusplus
+/* *INDENT-OFF* */
+extern "C" {
+/* *INDENT-ON* */
+#endif
+
+#define NSTACK_CONFIG_SHM "nstack_config"
+
+#define MBUF_DATA_SIZE 2048
+#define MBUF_SIZE (MBUF_DATA_SIZE + sizeof(struct common_mem_mbuf) + COMMON_PKTMBUF_HEADROOM)
+
+extern int g_nstack_bind_cpu;
+
+inline uint16_t spl_mbuf_refcnt_update (void *mbuf, int16_t value);
+struct stack_proc_content *get_stack_proc_content ();
+
+inline int spl_msg_malloc (data_com_msg ** p_msg_entry);
+#define spl_msg_free(p_msg_entry) msg_free(p_msg_entry)
+struct spl_pbuf *spl_mbuf_malloc (uint16_t len, spl_pbuf_type type,
+ u16_t * count);
+
+inline void spl_mbuf_free (void *mbuf);
+
+inline int spl_set_lcore_id (unsigned dest_lcore_id);
+
+static inline unsigned
+spl_get_lcore_id ()
+{
+ unsigned core_id = 0;
+
+#if (DPDK_MODULE != 1)
+#ifdef HAL_LIB
+#else
+ core_id = rte_lcore_id ();
+#endif
+ if (core_id >= MAX_THREAD_NUM)
+ {
+ core_id = 0;
+ }
+#endif
+
+ return core_id;
+}
+
+int set_share_config ();
+
+int init_instance ();
+
+void printmeminfo ();
+
+#ifdef __cplusplus
+/* *INDENT-OFF* */
+}
+/* *INDENT-ON* */
+#endif
+#endif /* SERVER_DPDK_H_ */
diff --git a/stacks/lwip_stack/lwip_src/include/netif/sharedmemory.h b/stacks/lwip_stack/lwip_src/include/netif/sharedmemory.h
new file mode 100644
index 0000000..0a92ea0
--- /dev/null
+++ b/stacks/lwip_stack/lwip_src/include/netif/sharedmemory.h
@@ -0,0 +1,148 @@
+/*
+*
+* Copyright (c) 2018 Huawei Technologies Co.,Ltd.
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at:
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+#ifndef SHAREDMEMORY_H_
+#define SHAREDMEMORY_H_
+#include "stackxopts.h"
+#include <sys/types.h>
+#include <semaphore.h>
+//#include "stackx/raw.h"
+#include "tcp.h"
+#include "udp.h"
+//#include "stackx/ip.h"
+#include "spl_err.h"
+#include "list.h"
+#include "arch/queue.h"
+#include "spl_opt.h"
+#include "stackx/spl_ip_addr.h"
+
+#include "stackx/spl_api.h"
+#include <arch/sys_arch.h>
+#include "common_mem_api.h"
+//#include "stackx/memp.h"
+#include "stackx_instance.h"
+
+#include "hal_api.h"
+#ifdef HAL_LIB
+#else
+#include "rte_ring.h"
+#endif
+
+/** Description for a task waiting in select */
+struct stackx_select_cb
+{
+ /** Pointer to the next waiting task */
+ union
+ {
+ struct stackx_select_cb *next;
+ PTR_ALIGN_TYPE next_a;
+ };
+
+ /** Pointer to the previous waiting task */
+ union
+ {
+ struct stackx_select_cb *prev;
+ PTR_ALIGN_TYPE prev_a;
+ };
+
+ /** semaphore to wake up a task waiting for select */
+ //sys_sem_t sem;
+ union
+ {
+ sys_sem_t_v1 sem;
+ PTR_ALIGN_TYPE sem_a;
+ };
+
+ /** readset passed to select */
+ fd_set readset;
+
+ /** writeset passed to select */
+ fd_set writeset;
+
+ /** unimplemented: exceptset passed to select */
+ fd_set exceptset;
+
+ /** don't signal the same semaphore twice: set to 1 when signalled */
+ volatile int sem_signalled;
+
+ uint8_t pad_64bit[4];
+};
+/** From epoll.h: Definition of struct stackx_sock and stackx_select_cb ---------End*/
+
+enum tcp_run_type
+{
+ TCP_MUTIPL_INSTANCE = 0,
+ TCP_MASTER_WORKER,
+ TCP_DISTRIBUTOR_WORKER,
+ TCP_RUN_TO_COMPETE,
+ TCP_PROC_TYPE_END
+};
+
+enum proc_run_type
+{
+ PROC_MAIN_RUN_TYPE = 0,
+ PROC_BACKUP_RUN_TYPE,
+ PROC_RUN_TYPE_END
+};
+
+struct linux_port_info
+{
+ char if_name[HAL_MAX_NIC_NAME_LEN];
+ char ip_addr_linux[18]; //uint32_t ip_addr_linux;
+ char mask_linux[18]; //uint32_t mask_linux;
+ char bcast_linux[18]; //uint32_t bcast_linux;
+ char mac_addr[20]; //struct ether_addr mac_addr;
+ hal_hdl_t hdl;
+};
+
+struct stackx_port_info
+{
+ struct stackx_port_info *next_use_port;
+ struct linux_port_info linux_ip;
+};
+
+struct stackx_port_zone
+{
+ unsigned int port_num;
+ unsigned int bonded_port_num;
+ struct stackx_port_info *stackx_one_port;
+};
+
+struct select_cb_entry
+{
+ struct stackx_select_cb select_cb;
+
+ union
+ {
+ sem_t semForSelect;
+ ALIGN_TYPE pad_64bit[4];
+ };
+
+ union
+ {
+ struct select_cb_entry *pre_empty_entry;
+ PTR_ALIGN_TYPE pre_empty_entry_a;
+ };
+
+ union
+ {
+ struct select_cb_entry *next_empty_entry;
+ PTR_ALIGN_TYPE next_empty_entry_a;
+ };
+
+};
+
+#endif /* SHAREDMEMORY_H_ */
diff --git a/stacks/lwip_stack/lwip_src/include/netif/spl_hal.h b/stacks/lwip_stack/lwip_src/include/netif/spl_hal.h
new file mode 100644
index 0000000..ca56663
--- /dev/null
+++ b/stacks/lwip_stack/lwip_src/include/netif/spl_hal.h
@@ -0,0 +1,114 @@
+/*
+*
+* Copyright (c) 2018 Huawei Technologies Co.,Ltd.
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at:
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+#ifndef SPL_HAL_H_
+#define SPL_HAL_H_
+
+#include "hal_api.h"
+#include "netif.h"
+#include "nsfw_maintain_api.h"
+#include "stackx_spl_share.h"
+#include "stackx_pbuf_comm.h"
+#include "netifapi.h"
+
+#ifdef __cplusplus
+/* *INDENT-OFF* */
+extern "C" {
+/* *INDENT-ON* */
+#endif
+
+int spl_hal_init (int argc, char *argv[]);
+int spl_hal_port_init ();
+
+int spl_hal_stats_display (struct netif *pnetif, char *str, u32_t len,
+ char *json, u32_t json_len);
+
+err_t spl_hal_output (struct netif *netif, struct pbuf *buf);
+void spl_hal_input (struct netif *netif, struct spl_pbuf **buf);
+
+inline u16_t spl_hal_recv (struct netif *netif, u8_t id);
+
+int spl_hal_tx_ip_cksum_enable ();
+int spl_hal_tx_udp_cksum_enable ();
+int spl_hal_tx_tcp_cksum_enable ();
+
+u32 spl_hal_is_nic_exist (const char *name);
+
+int spl_hal_is_bond_netif (struct netif *pnetif);
+
+static inline void
+spl_do_dump (struct spl_pbuf *p, u16 direction)
+{
+ struct spl_pbuf *q = p;
+ while (q)
+ {
+ ntcpdump (q->payload, q->len, direction);
+ q = q->next;
+ }
+}
+
+/* information of bond*/
+#define MAX_BOND_PORT_NUM 4
+
+/* information of one bond port */
+struct bond_set
+{
+ char bond_port_name[HAL_MAX_NIC_NAME_LEN];
+ char slave_ports[HAL_MAX_SLAVES_PER_BOND][HAL_MAX_NIC_NAME_LEN];
+ u8_t slave_port_cnt;
+};
+
+#define NETIF_ETH_ADDR_LEN 6
+
+/* information of all bond ports */
+struct bond_ports_info
+{
+ u8_t cnt;
+ struct bond_set ports[MAX_BOND_PORT_NUM];
+};
+
+struct ether_addr
+{
+ u8_t addr_bytes[NETIF_ETH_ADDR_LEN];
+
+};
+
+struct netif *get_netif_by_ip (unsigned int ip);
+struct netif *netif_check_broadcast_addr (spl_ip_addr_t * addr);
+struct netifExt *getNetifExt (u16_t id);
+int netifExt_add (struct netif *netif);
+
+int add_netif_ip (char *netif_name, unsigned int ip, unsigned int mask);
+int del_netif_ip (char *netif_name, unsigned int ip);
+
+err_t spl_netifapi_netif_add (struct netif *pnetif,
+ spl_ip_addr_t * ipaddr,
+ spl_ip_addr_t * netmask,
+ spl_ip_addr_t * gw,
+ void *state,
+ netif_init_fn init,
+ netif_input_fn input,
+ netifapi_void_fn voidfunc);
+struct netif *find_netif_by_if_name (char *if_name);
+void ethernetif_packets_input (struct netif *pstnetif);
+err_t ethernetif_init (struct netif *pnetif);
+
+#ifdef __cplusplus
+/* *INDENT-OFF* */
+}
+/* *INDENT-ON* */
+#endif
+#endif /* SPL_HAL_H_ */