/****************************************************************************** * drivers/wireless/spirit/spirit_pktstack.c * Configuration and management of SPIRIT STack packets. * * Copyright(c) 2015 STMicroelectronics * Author: VMA division - AMS * Version 3.2.2 08-July-2015 * * Adapted for NuttX by: * Author: Gregory Nutt * * Redistribution and use in source and binary forms, with or without modification, * are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * 3. Neither the name of STMicroelectronics nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * ******************************************************************************/ /****************************************************************************** * Included Files ******************************************************************************/ #include #include #include "spirit_spi.h" #include "spirit_pktcommon.h" #include "spirit_pktstack.h" /****************************************************************************** * Public Functions ******************************************************************************/ /****************************************************************************** * Name: spirit_pktstack_initialize * * Description: * Initializes the SPIRIT STack packet according to the specified parameters * in the struct spirit_pktstack_init_s. * * Input Parameters: * spirit - Reference to a Spirit library state structure instance * pktstack - STack packet init structure. * * Returned Value: * Zero (OK) on success; a negated errno value on failure. * ******************************************************************************/ int spirit_pktstack_initialize(FAR struct spirit_library_s *spirit, FAR const struct spirit_pktstack_init_s *pktstack) { uint8_t regval[4]; uint8_t pktlenwidth; int ret; int i; /* Check the parameters */ DEBUGASSERT(IS_PKT_PREAMBLE_LENGTH(pktstack->premblen)); DEBUGASSERT(IS_PKT_SYNC_LENGTH(pktstack->synclen)); DEBUGASSERT(IS_PKT_CRC_MODE(pktstack->crcmode)); DEBUGASSERT(IS_PKT_LENGTH_WIDTH_BITS(pktstack->pktlenwidth)); DEBUGASSERT(IS_PKT_CRC_MODE(pktstack->fixvarlen)); DEBUGASSERT(IS_SPIRIT_FUNCTIONAL_STATE(pktstack->fec)); DEBUGASSERT(IS_SPIRIT_FUNCTIONAL_STATE(pktstack->datawhite)); DEBUGASSERT(IS_PKT_CONTROL_LENGTH(pktstack->ctrllen)); /* Read the PROTOCOL1 register */ ret = spirit_reg_read(spirit, PROTOCOL1_BASE, ®val[0], 1); if (ret < 0) { return ret; } /* Mask a reserved bit */ regval[0] &= ~0x20; /* Always (!) set the automatic packet filtering */ regval[0] |= PROTOCOL1_AUTO_PCKT_FLT_MASK; /* Write the value to register */ ret = spirit_reg_write(spirit, PROTOCOL1_BASE, ®val[0], 1); if (ret < 0) { return ret; } /* Read the PCKT_FLT_OPTIONS register */ ret = spirit_reg_read(spirit, PCKT_FLT_OPTIONS_BASE, ®val[0], 1); if (ret < 0) { return ret; } /* Always reset the control and source filtering */ regval[0] &= ~(PCKT_FLT_OPTIONS_SOURCE_FILTERING_MASK | PCKT_FLT_OPTIONS_CONTROL_FILTERING_MASK); /* Write the value to register */ ret = spirit_reg_write(spirit, PCKT_FLT_OPTIONS_BASE, ®val[0], 1); if (ret < 0) { return ret; } /* Address and control length setting: source and destination address are * always present so ADDRESS_LENGTH=2 */ regval[0] = 0x10 | (uint8_t)pktstack->ctrllen; /* Packet format and width length setting */ pktlenwidth = pktstack->pktlenwidth; if (pktlenwidth == 0) { pktlenwidth = 1; } regval[1] = (uint8_t)PCKTCTRL3_PCKT_FRMT_STACK | (uint8_t)(pktlenwidth - 1); /* Preamble, sync and fixed or variable length setting */ regval[2] = ((uint8_t)pktstack->premblen) | ((uint8_t)pktstack->synclen) | ((uint8_t)pktstack->fixvarlen); /* CRC length, whitening and FEC setting */ regval[3] = (uint8_t) pktstack->crcmode; if (pktstack->datawhite == S_ENABLE) { regval[3] |= PCKTCTRL1_WHIT_MASK; } if (pktstack->fec == S_ENABLE) { regval[3] |= PCKTCTRL1_FEC_MASK; } /* Write registers */ ret = spirit_reg_write(spirit, PCKTCTRL4_BASE, regval, 4); if (ret < 0) { return ret; } /* Sync words setting */ for (i = 0; i < 4; i++) { if (i < 3 - (pktstack->synclen >> 1)) { regval[i] = 0; } else { regval[i] = (uint8_t) (pktstack->syncwords >> (8 * i)); } } /* Enables or disables the CRC check */ if (pktstack->crcmode == PKT_NO_CRC) { ret = spirit_pktcommon_enable_crcfilter(spirit, S_DISABLE); } else { ret = spirit_pktcommon_enable_crcfilter(spirit, S_ENABLE); } if (ret < 0) { return ret; } /* Write registers */ return spirit_reg_write(spirit, SYNC4_BASE, regval, 4); } /****************************************************************************** * Name: spirit_pktstack_get_setup * Description: * Returns the SPIRIT STack packet structure according to the specified * parameters in the registers. * * Input Parameters: * spirit - Reference to a Spirit library state structure instance * pktstack - STack packet init structure. * * Returned Value: * Zero (OK) on success; a negated errno value on failure. * ******************************************************************************/ int spirit_pktstack_get_setup(FAR struct spirit_library_s *spirit, FAR struct spirit_pktstack_init_s *pktstack) { uint8_t regval[10]; int ret; int i; /* Read registers */ ret = spirit_reg_read(spirit, PCKTCTRL4_BASE, regval, 10); if (ret >= 0) { /* Length width */ pktstack->pktlenwidth = (regval[1] & 0x0f) + 1; /* Control length */ pktstack->ctrllen = regval[0] & 0x07; /* CRC mode */ pktstack->crcmode = regval[3] & 0xe0; /* Whitening */ pktstack->datawhite = (regval[3] >> 4) & 0x01; /* FEC */ pktstack->fec = regval[3] & 0x01; /* FIX or VAR bit */ pktstack->fixvarlen = regval[2] & 0x01; /* Preamble length */ pktstack->premblen = regval[2] & 0xf8; /* Sync length */ pktstack->synclen = regval[2] & 0x06; /* sync Words */ pktstack->syncwords = 0; for (i = 0; i < 4; i++) { if (i > 2 - (pktstack->synclen >> 1)) { pktstack->syncwords |= regval[i + 6] << (8 * i); } } } return OK; } /****************************************************************************** * Name: spirit_pktstack_address_initialize * * Description: * Initializes the SPIRIT STack packet addresses according to the specified * parameters in the PktStackAddresses struct. * * Input Parameters: * spirit - Reference to a Spirit library state structure instance * addrinit - STack packet addresses init structure. * * Returned Value: * Zero (OK) on success; a negated errno value on failure. * ******************************************************************************/ int spirit_pktstack_address_initialize(FAR struct spirit_library_s *spirit, FAR const struct spirit_pktstack_address_s *addrinit) { uint8_t regval[3]; int ret; /* Check the parameters */ DEBUGASSERT(IS_SPIRIT_FUNCTIONAL_STATE(addrinit->destfilter)); DEBUGASSERT(IS_SPIRIT_FUNCTIONAL_STATE(addrinit->mcastfilter)); DEBUGASSERT(IS_SPIRIT_FUNCTIONAL_STATE(addrinit->bcastfilter)); /* Read the filtering options ragister */ ret = spirit_reg_read(spirit, PCKT_FLT_OPTIONS_BASE, ®val[0], 1); if (ret >= 0) { /* Enables or disables filtering on my address */ if (addrinit->destfilter == S_ENABLE) { regval[0] |= PCKT_FLT_OPTIONS_DEST_VS_TX_ADDR_MASK; } else { regval[0] &= ~PCKT_FLT_OPTIONS_DEST_VS_TX_ADDR_MASK; } /* Enables or disables filtering on multicast address */ if (addrinit->mcastfilter == S_ENABLE) { regval[0] |= PCKT_FLT_OPTIONS_DEST_VS_MULTICAST_ADDR_MASK; } else { regval[0] &= ~PCKT_FLT_OPTIONS_DEST_VS_MULTICAST_ADDR_MASK; } /* Enables or disables filtering on broadcast address */ if (addrinit->bcastfilter == S_ENABLE) { regval[0] |= PCKT_FLT_OPTIONS_DEST_VS_BROADCAST_ADDR_MASK; } else { regval[0] &= ~PCKT_FLT_OPTIONS_DEST_VS_BROADCAST_ADDR_MASK; } /* Write values to the register */ ret = spirit_reg_write(spirit, PCKT_FLT_OPTIONS_BASE, ®val[0], 1); if (ret >= 0) { /* Fill array with the addresses passed in the structure */ regval[0] = addrinit->bcastaddr; regval[1] = addrinit->mcastaddr; regval[2] = addrinit->srcaddr; /* Write them to the addresses registers */ ret = spirit_reg_write(spirit, PCKT_FLT_GOALS_BROADCAST_BASE, regval, 3); } } return ret; } /****************************************************************************** * Name: spirit_pktstack_get_addrsetup * * Description: * Returns the SPIRIT STack packet addresses structure according to the * specified parameters in the registers. * * Input Parameters: * spirit - Reference to a Spirit library state structure instance * addrinit - STack packet addresses init structure. * * Returned Value: * Zero (OK) on success; a negated errno value on failure. * ******************************************************************************/ int spirit_pktstack_get_addrsetup(FAR struct spirit_library_s *spirit, FAR struct spirit_pktstack_address_s *addrinit) { uint8_t regval[3]; int ret; /* Read values from the PCKT_FLT_GOALS registers */ ret = spirit_reg_read(spirit, PCKT_FLT_GOALS_BROADCAST_BASE, regval, 3); if (ret >= 0) { /* Fit the structure with the read addresses */ addrinit->bcastaddr = regval[0]; addrinit->mcastaddr = regval[1]; addrinit->srcaddr = regval[2]; ret = spirit_reg_read(spirit, PCKT_FLT_OPTIONS_BASE, ®val[0], 1); if (ret >= 0) { /* Fit the structure with the read filtering bits */ addrinit->bcastfilter = (enum spirit_functional_state_e)((regval[0] >> 1) & 0x01); addrinit->mcastfilter = (enum spirit_functional_state_e)((regval[0] >> 2) & 0x01); addrinit->destfilter = (enum spirit_functional_state_e)((regval[0] >> 3) & 0x01); } } return ret; } /****************************************************************************** * Name: spirit_pktstack_llp_initialize * * Description: * Initializes the SPIRIT STack packet LLP options according to the specified * parameters in the struct spirit_pktstack_llp_s struct. * * Input Parameters: * spirit - Reference to a Spirit library state structure instance * llpinit - STack packet LLP init structure. * * Returned Value: * Zero (OK) on success; a negated errno value on failure. * ******************************************************************************/ int spirit_pktstack_llp_initialize(FAR struct spirit_library_s *spirit, FAR const struct spirit_pktstack_llp_s *llpinit) { uint8_t regval[2]; int ret; /* Check the parameters */ DEBUGASSERT(IS_SPIRIT_FUNCTIONAL_STATE(llpinit->piggyback)); DEBUGASSERT(IS_SPIRIT_FUNCTIONAL_STATE(llpinit->autoack)); DEBUGASSERT(IS_PKT_MAXRETX(llpinit->maxretx)); /* Check if piggybacking is enabled and autoack is disabled */ DEBUGASSERT(!(llpinit->piggyback == S_ENABLE && llpinit->autoack == S_DISABLE)); /* Piggybacking mechanism setting from the PROTOCOL1 register */ ret = spirit_reg_read(spirit, PROTOCOL1_BASE, regval, 2); if (ret >= 0) { if (llpinit->piggyback == S_ENABLE) { regval[0] |= PROTOCOL1_PIGGYBACKING_MASK; } else { regval[0] &= ~PROTOCOL1_PIGGYBACKING_MASK; } /* RX and TX autoack mechanisms setting from the PROTOCOL0 register */ if (llpinit->autoack == S_ENABLE) { regval[1] |= PROTOCOL0_AUTO_ACK_MASK; } else { regval[1] &= ~PROTOCOL0_AUTO_ACK_MASK; } /* Max number of retransmission setting */ regval[1] &= ~PROTOCOL0_NMAX_RETX_MASK; regval[1] |= llpinit->maxretx; /* Write registers */ ret = spirit_reg_write(spirit, PROTOCOL1_BASE, regval, 2); } return ret; } /****************************************************************************** * Name: spirit_pktstack_get_llpsetup * * Description: * Returns the SPIRIT STack packet LLP options according to the specified * values in the registers. * * Input Parameters: * spirit - Reference to a Spirit library state structure instance * llpinit - STack packet LLP structure. * * Returned Value: * Zero (OK) on success; a negated errno value on failure. * ******************************************************************************/ int spirit_pktstack_get_llpsetup(FAR struct spirit_library_s *spirit, FAR struct spirit_pktstack_llp_s *llpinit) { uint8_t regval[2]; int ret; /* Piggybacking mechanism setting from the PROTOCOL1 register */ ret = spirit_reg_read(spirit, PROTOCOL1_BASE, regval, 2); if (ret >= 0) { /* Fit the structure with the read values */ llpinit->piggyback = (regval[0] >> 6) & 0x01; llpinit->autoack = (regval[1] >> 2) & 0x01; llpinit->maxretx = regval[1] & PROTOCOL0_NMAX_RETX_MASK; } return ret; } /****************************************************************************** * Name: spirit_pktstack_set_format * * Description: * Configures the Stack packet format for SPIRIT. * * Input Parameters: * spirit - Reference to a Spirit library state structure instance * * Returned Value: * Zero (OK) on success; a negated errno value on failure. * ******************************************************************************/ int spirit_pktstack_set_format(FAR struct spirit_library_s *spirit) { uint8_t regval; int ret; /* Read the PCKTCTRL3 register value */ ret = spirit_reg_read(spirit, PCKTCTRL3_BASE, ®val, 1); if (ret < 0) { return ret; } /* Build value to be written. Also set to 0 the direct RX mode bits */ regval &= 0x0f; regval |= ((uint8_t) PCKTCTRL3_PCKT_FRMT_STACK); /* Write the value to the PCKTCTRL3 register. */ ret = spirit_reg_write(spirit, PCKTCTRL3_BASE, ®val, 1); if (ret < 0) { return ret; } /* Read the PCKTCTRL1 register value */ ret = spirit_reg_read(spirit, PCKTCTRL1_BASE, ®val, 1); if (ret < 0) { return ret; } /* Build the new value. Set to 0 the direct TX mode bits */ regval &= 0xf3; /* Write the PCKTCTRL1 value to register */ ret = spirit_reg_write(spirit, PCKTCTRL1_BASE, ®val, 1); if (ret < 0) { return ret; } /* Read the PROTOCOL1 register */ ret = spirit_reg_read(spirit, PROTOCOL1_BASE, ®val, 1); if (ret < 0) { return ret; } /* Mask a reserved bit */ regval &= ~0x20; /* Write the value to the PROTOCOL1 register */ return spirit_reg_write(spirit, PROTOCOL1_BASE, ®val, 1); } /****************************************************************************** * Name: spirit_pktstack_enable_addrlen * * Description: * Sets the address length for SPIRIT STack packets (always 2). * * Input Parameters: * spirit - Reference to a Spirit library state structure instance * * Returned Value: * Zero (OK) on success; a negated errno value on failure. * ******************************************************************************/ int spirit_pktstack_enable_addrlen(FAR struct spirit_library_s *spirit) { uint8_t regval; int ret; /* Read the PCKTCTRL4 register value */ ret = spirit_reg_read(spirit, PCKTCTRL4_BASE, ®val, 1); if (ret >= 0) { /* Build the new value */ regval &= ~PCKTCTRL4_ADDRESS_LEN_MASK; regval |= ((uint8_t) 0x10); /* Write the value to the PCKTCTRL4 register */ ret = spirit_reg_write(spirit, PCKTCTRL4_BASE, ®val, 1); } return ret; } /****************************************************************************** * Name: spirit_pktstack_set_payloadlen * * Description: * Sets the payload length for SPIRIT STack packets. Since the packet length * depends from the address (always 2 for this packet format) and the * control field size, this function reads the control length register * content in order to determine the correct packet length to be written. * * Input Parameters: * spirit - Reference to a Spirit library state structure instance * payloadlen - Payload length in bytes. * * Returned Value: * Zero (OK) on success; a negated errno value on failure. * ******************************************************************************/ int spirit_pktstack_set_payloadlen(FAR struct spirit_library_s *spirit, uint16_t payloadlen) { uint8_t regval[2]; /* Computes the oversize (address + control) size */ uint16_t oversize = 2 + (uint16_t)spirit_pktcommon_get_controllen(spirit); /* Computes PCKTLEN0 value from lPayloadLength */ regval[1] = BUILD_PCKTLEN0(payloadlen + oversize); /* Computes PCKTLEN1 value from lPayloadLength */ regval[0] = BUILD_PCKTLEN1(payloadlen + oversize); /* Write the value to the PCKTLENx registers */ return spirit_reg_write(spirit, PCKTLEN1_BASE, regval, 2); } /****************************************************************************** * Name: spirit_pktstack_get_payloadlen * * Description: * Returns the payload length for SPIRIT STack packets. Since the packet * length depends from the address and the control field size, this function * reads the correspondent registers in order to determine the correct * payload length to be returned. * * Input Parameters: * None. * * Returned Value: * Payload length. * ******************************************************************************/ uint16_t spirit_pktstack_get_payloadlen(FAR struct spirit_library_s *spirit) { uint8_t regval[2]; uint16_t oversize; /* Computes the oversize (address + control) size */ oversize = 2 + (uint16_t)spirit_pktcommon_get_controllen(spirit); /* Read the PCKTLEN1 registers value */ (void)spirit_reg_read(spirit, PCKTLEN1_BASE, regval, 2); /* Rebuild and return the payload length value */ return ((((uint16_t) regval[1]) << 8) + (uint16_t) regval[0] - oversize); } /****************************************************************************** * Name: spirit_pktstack_set_varlen * * Description: * Computes and sets the variable payload length for SPIRIT STack packets. * * Input Parameters: * spirit - Reference to a Spirit library state structure instance * payloadlen - Payload length in bytes. * ctrllen - Control length in bytes. * * Returned Value: * Zero (OK) on success; a negated errno value on failure. * ******************************************************************************/ int spirit_pktstack_set_varlen(FAR struct spirit_library_s *spirit, uint16_t payloadlen, enum pkt_ctrllen_e ctrllen) { uint32_t pktlen; uint8_t regval; int ret; int i; /* packet length = payload length + address length (2) + control length */ pktlen = payloadlen + 2 + ctrllen; /* Computes the number of bits */ for (i = 0; i < 16; i++) { if (pktlen == 0) { break; } pktlen >>= 1; } if (i == 0) { i = 1; } /* Read the PCKTCTRL3 register value */ ret = spirit_reg_read(spirit, PCKTCTRL3_BASE, ®val, 1); if (ret >= 0) { /* Build the register value */ regval &= ~PCKTCTRL3_LEN_WID_MASK; regval |= (uint8_t)(i - 1); /* Write the PCKTCTRL3 register value */ ret = spirit_reg_write(spirit, PCKTCTRL3_BASE, ®val, 1); } return ret; } /****************************************************************************** * Name: spirit_pkstack_set_rxsource_addrmask * * Description: * Rx packet source mask. Used to mask the address of the accepted packets. * If 0 -> no filtering. * * Input Parameters: * spirit - Reference to a Spirit library state structure instance * mask - Rx source mask. * * Returned Value: * Zero (OK) on success; a negated errno value on failure. * ******************************************************************************/ int spirit_pkstack_set_rxsource_addrmask(FAR struct spirit_library_s *spirit, uint8_t mask) { /* Write value to the register PCKT_FLT_GOALS_SOURCE_MASK */ return spirit_reg_write(spirit, PCKT_FLT_GOALS_SOURCE_MASK_BASE, &mask, 1); } /****************************************************************************** * Name: spirit_pktstack_get_rxsource_addrmask * * Description: * Returns the Rx packet source mask. Used to mask the address of the * accepted packets. If 0 -> no filtering. * * Input Parameters: * spirit - Reference to a Spirit library state structure instance * * Returned Value: * Rx source mask. * ******************************************************************************/ uint8_t spirit_pktstack_get_rxsource_addrmask(FAR struct spirit_library_s *spirit) { uint8_t regval; /* Write value to the PCKT_FLT_GOALS_SOURCE_MASK register */ (void)spirit_reg_read(spirit, PCKT_FLT_GOALS_SOURCE_MASK_BASE, ®val, 1); /* Return the read value */ return regval; } /****************************************************************************** * Name: spirit_pkstack_get_rxpktlen * * Description: * Returns the packet length field of the received packet. * * Input Parameters: * spirit - Reference to a Spirit library state structure instance * * Returned Value: * Packet length. * ******************************************************************************/ uint16_t spirit_pkstack_get_rxpktlen(FAR struct spirit_library_s *spirit) { uint8_t regval[2]; uint16_t pktlen; /* Read the RX_PCKT_LENx registers value */ (void)spirit_reg_read(spirit, RX_PCKT_LEN1_BASE, regval, 2); /* Rebuild and return the the length field */ pktlen = ((((uint16_t) regval[0]) << 8) + (uint16_t) regval[1]); /* Computes the oversize (address + control) size */ pktlen -= 2 + (uint16_t)spirit_pktcommon_get_controllen(spirit); return pktlen; } /****************************************************************************** * Name: spirit_pkstack_enable_rxsource_addrfilter * * Description: * If enabled RX packet is accepted only if the masked source address field * matches the masked source address field reference (SOURCE_MASK & * SOURCE_FIELD_REF == SOURCE_MASK & RX_SOURCE_FIELD). * * NOTE: This filtering control is enabled by default but the source * address mask is by default set to 0. As a matter of fact the user has * to enable the source filtering bit after the packet initialization * because the packet initialiazation routine disables it. * * Input Parameters: * spirit - Reference to a Spirit library state structure instance * newstate - New state for Source address filtering enable bit. * This parameter can be S_ENABLE or S_DISABLE. * * Returned Value: * Zero (OK) on success; a negated errno value on failure. * ******************************************************************************/ int spirit_pkstack_enable_rxsource_addrfilter(FAR struct spirit_library_s *spirit, enum spirit_functional_state_e newstate) { uint8_t regval; int ret; /* Check the parameters */ DEBUGASSERT(IS_SPIRIT_FUNCTIONAL_STATE(newstate)); /* Modify the register value: set or reset the source bit filtering */ ret = spirit_reg_read(spirit, PCKT_FLT_OPTIONS_BASE, ®val, 1); if (ret >= 0) { /* Set or reset the SOURCE ADDRESS filtering enabling bit */ if (newstate == S_ENABLE) { regval |= PCKT_FLT_OPTIONS_SOURCE_FILTERING_MASK; } else { regval &= ~PCKT_FLT_OPTIONS_SOURCE_FILTERING_MASK; } /* Write the new value to the PCKT_FLT_OPTIONS register */ ret = spirit_reg_write(spirit, PCKT_FLT_OPTIONS_BASE, ®val, 1); } return ret; }