nuttx/drivers/wireless/cc3000/socket.c
Gregory Nutt e42f7b552f This commit is a set of patches 02/11 through 03/11 correcting issues with the CC3000 networking (01/11 was committed separated). Among these 10 patches:
03/11: CC3000 driver was getting stuck at recv() when remote host closed connection and application tried to read data from remotely shutdown socket. This patch adds proper handling for remotely closed socket event.

07/11: Socket state initialization was done in 'register', while it should be initialized in 'open' and deinitialized in 'close'. Old way caused problems when device is closed, power-cycled and then reopened as old socket state was left enabled.

08/11: Select thread was getting stuck after 'close, power-cycle, reopen', since selectsem was not properly setup and cleaned up.

09/11 'maxFD' was not properly reset in select worker and not checked for before calling cc3000_select().

10/11: After wlan_stop()/cc3000_close(), irqsem was left with count '-1'. Therefore on next wlan_start()/cc3000_open(), initial value for irqsem was wrong. Additional repeated wlan_start()/wlan_stop() decreased irqsem value further. Obviously this causes driver not to function correctly and freeze.

Patch moves initialization and destruction of waitsem, irqsem and readysem to cc3000_open/cc3000_close.

All are: Signed-off-by: Jussi Kivilinna <jussi.kivilinna@haltian.com>
2014-11-28 08:52:52 -06:00

808 lines
27 KiB
C

/*****************************************************************************
* socket.c - CC3000 Host Driver Implementation.
* Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com/
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 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.
*
* Neither the name of Texas Instruments Incorporated 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
* OWNER 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 <sys/types.h>
#include <sys/select.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <debug.h>
#include <stdlib.h>
#include <pthread.h>
#include <nuttx/wireless/cc3000/include/sys/socket.h>
#include "cc3000_socket.h"
#include "cc3000drv.h"
#include "cc3000.h"
/*****************************************************************************
* Pre-processor Definitions
*****************************************************************************/
#ifndef ARRAY_SIZE
# define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
#endif
#define waitlldbg(x,...) // lldbg
/****************************************************************************
* Private Types
****************************************************************************/
/****************************************************************************
* Private Function Prototypes
****************************************************************************/
/****************************************************************************
* Public Data
****************************************************************************/
pthread_mutex_t g_cc3000_mut;
/****************************************************************************
* Private Data
****************************************************************************/
static const int bsd2ti_types[] =
{
CC3000_SOCK_STREAM, /* SOCK_STREAM */
CC3000_SOCK_DGRAM, /* SOCK_DGRAM */
CC3000_SOCK_SEQPACKET, /* SOCK_SEQPACKET */
CC3000_SOCK_RAW, /* SOCK_RAW */
CC3000_SOCK_RDM /* SOCK_RDM */
};
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Public Functions
****************************************************************************/
/*****************************************************************************
* Name: socket
*
* Description:
* create an endpoint for communication. The socket function creates a
* socket that is bound to a specific transport service provider. This
* function is called by the application layer to obtain a socket handle.
*
* Input Parameters:
* domain selects the protocol family which will be used for
* communication. On this version only AF_INET is supported
* type specifies the communication semantics. On this version
* only SOCK_STREAM, SOCK_DGRAM, SOCK_RAW are supported
* protocol specifies a particular protocol to be used with the
* socket IPPROTO_TCP, IPPROTO_UDP or IPPROTO_RAW are
* supported.
*
* Returned Value:
* On success, socket handle that is used for consequent socket
* operations. On error, -1 is returned.
*
*****************************************************************************/
int cc3000_socket(int domain, int type, int protocol)
{
int sd;
if (type < SOCK_STREAM || type >= ARRAY_SIZE(bsd2ti_types))
{
errno = EPROTOTYPE;
return -1;
}
switch (domain)
{
case AF_INET:
domain = CC3000_AF_INET;
break;
case AF_INET6:
domain = CC3000_AF_INET6;
break;
default:
errno = EAFNOSUPPORT;
return -1;
}
switch (protocol)
{
case CC3000_IPPROTO_IP:
case CC3000_IPPROTO_ICMP:
case CC3000_IPPROTO_TCP:
case CC3000_IPPROTO_UDP:
case CC3000_IPPROTO_IPV6:
case CC3000_IPPROTO_NONE:
case CC3000_IPPROTO_TX_TEST_RAW:
case CC3000_IPPROTO_RAW:
case CC3000_IPPROTO_MAX:
break;
default:
errno = EPROTONOSUPPORT;
return -1;
}
cc3000_lib_lock();
type = bsd2ti_types[type];
sd = cc3000_socket_impl(domain,type,protocol);
#ifdef CONFIG_CC3000_MT
cc3000_add_socket(sd);
#endif
cc3000_lib_unlock();
return sd;
}
/*****************************************************************************
* Name: closesocket
*
* Description:
* The socket function closes a created socket.
*
* Input Parameters:
* sockfd socket handle.
*
* Returned Value:
* On success, zero is returned. On error, -1 is returned.
*
*****************************************************************************/
int cc3000_closesocket(int sockfd)
{
int ret;
#ifdef CONFIG_CC3000_MT
waitlldbg("remove\n");
cc3000_remove_socket(sockfd);
#endif
cc3000_lib_lock();
waitlldbg("Call closesocketl\n");
ret = cc3000_closesocket_impl(sockfd);
waitlldbg("return closesocket\n");
cc3000_lib_unlock();
return ret;
}
/*****************************************************************************
* Name: accept, cc3000_do_accept
*
* Description:
* accept a connection on a socket:
* This function is used with connection-based socket types
* (SOCK_STREAM). It extracts the first connection request on the
* queue of pending connections, creates a new connected socket, and
* returns a new file descriptor referring to that socket.
* The newly created socket is not in the listening state.
* The original socket sd is unaffected by this call.
* The argument sd is a socket that has been created with socket(),
* bound to a local address with bind(), and is listening for
* connections after a listen(). The argument addr is a pointer
* to a sockaddr structure. This structure is filled in with the
* address of the peer socket, as known to the communications layer.
* The exact format of the address returned addr is determined by the
* socket's address family. The addrlen argument is a value-result
* argument: it should initially contain the size of the structure
* pointed to by addr, on return it will contain the actual
* length (in bytes) of the address returned.
*
* Input Parameters:
* sockfd socket descriptor (handle)
* addr the argument addr is a pointer to a sockaddr structure
* This structure is filled in with the address of the
* peer socket, as known to the communications layer.
* determined. The exact format of the address returned
* addr is by the socket's address sockaddr.
* On this version only AF_INET is supported.
* This argument returns in network order.
* addrlen The addrlen argument is a value-result argument:
* it should initially contain the size of the structure
* pointed to by addr.
*
* Returned Value:
* For socket in blocking mode:
* On success, socket handle. on failure negative
* For socket in non-blocking mode:
* - On connection establishment, socket handle
* - On connection pending, SOC_IN_PROGRESS (-2)
* - On failure, SOC_ERROR (-1)
*
*****************************************************************************/
int cc3000_do_accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen)
{
int ret;
cc3000_lib_lock();
ret = cc3000_accept_impl(sockfd, addr, addrlen);
cc3000_lib_unlock();
return ret;
}
int cc3000_accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen)
#ifdef CONFIG_CC3000_MT
{
short non_blocking = CC3000_SOCK_ON;
if (setsockopt(sockfd, CC3000_SOL_SOCKET, CC3000_SOCKOPT_ACCEPT_NONBLOCK,
&non_blocking, sizeof(non_blocking)) < 0)
{
ndbg("setsockopt failure %d\n", errno);
return -errno;
}
memset(addr,0,*addrlen);
return cc3000_accept_socket(sockfd,addr,addrlen);
}
#else
{
cc3000_accept_socket(sockfd,0);
short nonBlocking = CC3000_SOCK_OFF;
if (setsockopt(sockfd, CC3000_SOL_SOCKET, CC3000_SOCKOPT_ACCEPT_NONBLOCK,
&nonBlocking, sizeof(nonBlocking)) < 0)
{
ndbg("setsockopt failure %d\n", errno);
return -errno;
}
return cc3000_do_accept(int sockfd, addr, addrlen);;
}
#endif
/*****************************************************************************
* Name: bind
*
* Description:
* assign a name to a socket
* This function gives the socket the local address addr.
* addr is addrlen bytes long. Traditionally, this is called when a
* socket is created with socket, it exists in a name space (address
* family) but has no name assigned.
* It is necessary to assign a local address before a SOCK_STREAM
* socket may receive connections.
*
* Input Parameters:
* sockfd socket descriptor (handle)
* addr specifies the destination address. On this version
* only AF_INET is supported.
* addrlen contains the size of the structure pointed to by addr.
*
* Returned Value:
* On success, zero is returned. On error, -1 is returned.
*
*****************************************************************************/
int cc3000_bind(int sockfd, FAR const struct sockaddr *addr, socklen_t addrlen)
{
int ret;
cc3000_lib_lock();
ret = cc3000_bind_impl(sockfd, addr, addrlen);
cc3000_lib_unlock();
return ret;
}
/*****************************************************************************
* Name: listen
*
* Description:
* listen for connections on a socket
* The willingness to accept incoming connections and a queue
* limit for incoming connections are specified with listen(),
* and then the connections are accepted with accept.
* The listen() call applies only to sockets of type SOCK_STREAM
* The backlog parameter defines the maximum length the queue of
* pending connections may grow to.
*
* NOTE: On this version, backlog is not supported
*
* Input Parameters:
* sockfd socket descriptor (handle)
* backlog specifies the listen queue depth. On this version
* backlog is not supported.
*
* Returned Value:
* On success, zero is returned. On error, -1 is returned.
*
*****************************************************************************/
int cc3000_listen(int sockfd, int backlog)
{
int ret;
cc3000_lib_lock();
ret = cc3000_listen_impl(sockfd,backlog);
cc3000_lib_unlock();
return ret;
}
/*****************************************************************************
* Name: connect
*
* Description:
* initiate a connection on a socket
* Function connects the socket referred to by the socket descriptor
* sd, to the address specified by addr. The addrlen argument
* specifies the size of addr. The format of the address in addr is
* determined by the address space of the socket. If it is of type
* SOCK_DGRAM, this call specifies the peer with which the socket is
* to be associated; this address is that to which datagrams are to be
* sent, and the only address from which datagrams are to be received.
* If the socket is of type SOCK_STREAM, this call attempts to make a
* connection to another socket. The other socket is specified by
* address, which is an address in the communications space of the
* socket. Note that the function implements only blocking behavior
* thus the caller will be waiting either for the connection
* establishment or for the connection establishment failure.
*
* Input Parameters:
* sockfd socket descriptor (handle)
* addr specifies the destination addr. On this version
* only AF_INET is supported.
* addrlen contains the size of the structure pointed to by addr
*
* Returned Value:
* On success, zero is returned. On error, -1 is returned
*
*****************************************************************************/
int cc3000_connect(int sockfd, FAR const struct sockaddr *addr, socklen_t addrlen)
{
int ret;
cc3000_lib_lock();
ret = cc3000_connect_impl(sockfd, addr, addrlen);
cc3000_lib_unlock();
return ret;
}
/*****************************************************************************
* Name: select
*
* Description:
* Monitor socket activity
* Select allow a program to monitor multiple file descriptors,
* waiting until one or more of the file descriptors become
* "ready" for some class of I/O operation
*
* NOTE: If the timeout value set to less than 5ms it will automatically set
* to 5ms to prevent overload of the system
*
* Input Parameters:
* nfds the highest-numbered file descriptor in any of the
* three sets, plus 1.
* readfds socket descriptors list for read monitoring
* writefds socket descriptors list for write monitoring
* exceptfds socket descriptors list for exception monitoring
* timeout is an upper bound on the amount of time elapsed
* before select() returns. Null means infinity
* timeout. The minimum timeout is 5 milliseconds,
* less than 5 milliseconds will be set
* automatically to 5 milliseconds.
*
* Returned Value:
* On success, select() returns the number of file descriptors
* contained in the three returned descriptor sets (that is, the
* total number of bits that are set in readfds, writefds,
* exceptfds) which may be zero if the timeout expires before
* anything interesting happens.
* On error, -1 is returned.
* *readfds - return the sockets on which Read request will
* return without delay with valid data.
* *writefds - return the sockets on which Write request
* will return without delay.
* *exceptfds - return the sockets which closed recently.
*
*****************************************************************************/
int cc3000_select(int nfds, fd_set *readfds, fd_set *writefds,fd_set *exceptfds,
struct timeval *timeout)
{
int ret;
cc3000_lib_lock();
ret = cc3000_select_impl(nfds, (TICC3000fd_set *)readfds,
(TICC3000fd_set *)writefds,
(TICC3000fd_set *)exceptfds, timeout);
cc3000_lib_unlock();
return ret;
}
#ifndef CC3000_TINY_DRIVER
/*****************************************************************************
* Name: setsockopt
*
* Description:
* set socket options
* This function manipulate the options associated with a socket.
* Options may exist at multiple protocol levels; they are always
* present at the uppermost socket level.
* When manipulating socket options the level at which the option
* resides and the name of the option must be specified.
* To manipulate options at the socket level, level is specified as
* SOL_SOCKET. To manipulate options at any other level the protocol
* number of the appropriate protocol controlling the option is
* supplied. For example, to indicate that an option is to be
* interpreted by the TCP protocol, level should be set to the
* protocol number of TCP;
* The parameters value and value_len are used to access value -
* use for setsockopt(). For getsockopt() they identify a buffer
* in which the value for the requested option(s) are to
* be returned. For getsockopt(), value_len is a value-result
* parameter, initially containing the size of the buffer
* pointed to by option_value, and modified on return to
* indicate the actual size of the value returned. If no option
* value is to be supplied or returned, option_value may be NULL.
*
* NOTE: On this version the following two socket options are enabled:
* The only protocol level supported in this version
* is SOL_SOCKET (level).
*
* 1. SOCKOPT_RECV_TIMEOUT (option)
* SOCKOPT_RECV_TIMEOUT configures recv and recvfrom timeout
* in milliseconds. In that case value should be pointer to
* unsigned long.
* 2. SOCKOPT_NONBLOCK (option). sets the socket non-blocking mode on
* or off. In that case value should be SOCK_ON or SOCK_OFF (value).
*
* Input Parameters:
* sockfd socket handle
* level defines the protocol level for this option
* option defines the option name to Interrogate
* value specifies a value for the option
* value_len specifies the length of the option value
*
* Returned Value:
* On success, zero is returned. On error, -1 is returned
*
*****************************************************************************/
int cc3000_setsockopt(int sockfd, int level, int option,
FAR const void *value, socklen_t value_len)
{
int ret;
cc3000_lib_lock();
ret = cc3000_setsockopt_impl(sockfd, level, option, value, value_len);
cc3000_lib_unlock();
return ret;
}
#endif
/*****************************************************************************
* Name: getsockopt
*
* Description:
* set socket options
* This function manipulate the options associated with a socket.
* Options may exist at multiple protocol levels; they are always
* present at the uppermost socket level.
* When manipulating socket options the level at which the option
* resides and the name of the option must be specified.
* To manipulate options at the socket level, level is specified as
* SOL_SOCKET. To manipulate options at any other level the protocol
* number of the appropriate protocol controlling the option is
* supplied. For example, to indicate that an option is to be
* interpreted by the TCP protocol, level should be set to the
* protocol number of TCP;
* The parameters value and value_len are used to access value -
* use for setsockopt(). For getsockopt() they identify a buffer
* in which the value for the requested option(s) are to
* be returned. For getsockopt(), value_len is a value-result
* parameter, initially containing the size of the buffer
* pointed to by option_value, and modified on return to
* indicate the actual size of the value returned. If no option
* value is to be supplied or returned, option_value may be NULL.
*
* NOTE: On this version the following two socket options are enabled:
* The only protocol level supported in this version
* is SOL_SOCKET (level).
* 1. SOCKOPT_RECV_TIMEOUT (option)
* SOCKOPT_RECV_TIMEOUT configures recv and recvfrom timeout
* in milliseconds.
* In that case value should be pointer to unsigned long.
* 2. SOCKOPT_NONBLOCK (option). sets the socket non-blocking mode on
* or off.
* In that case value should be SOCK_ON or SOCK_OFF (value).
*
* Input Parameters:
* sockfd socket handle
* level defines the protocol level for this option
* option defines the option name to Interrogate
* value specifies a value for the option
* value_len specifies the length of the option value
*
* Returned Value:
* On success, zero is returned. On error, -1 is returned
*
*****************************************************************************/
int cc3000_getsockopt(int sockfd, int level, int option, FAR void *value,
FAR socklen_t *value_len)
{
int ret;
cc3000_lib_lock();
ret = cc3000_getsockopt_impl(sockfd, level, option, value, value_len);
cc3000_lib_unlock();
return ret;
}
/*****************************************************************************
* Name: recv
*
* Description:
* function receives a message from a connection-mode socket
*
* NOTE: On this version, only blocking mode is supported.
*
* Input Parameters:
* sockfd socket handle
* buf Points to the buffer where the message should be stored
* len Specifies the length in bytes of the buffer pointed to
* by the buffer argument.
* flags Specifies the type of message reception.
* On this version, this parameter is not supported.
*
* Returned Value:
* Return the number of bytes received, or -1 if an error
* occurred
*
*****************************************************************************/
ssize_t cc3000_recv(int sockfd, FAR void *buf, size_t len, int flags)
{
ssize_t ret;
#ifdef CONFIG_CC3000_MT
waitlldbg("wait\n");
ret = cc3000_wait_data(sockfd);
waitlldbg("wait %d\n", ret);
if (ret == -ECONNABORTED)
{
return 0;
}
if (ret != OK)
{
return -1;
}
#endif
cc3000_lib_lock();
waitlldbg("recv\n");
ret = cc3000_recv_impl(sockfd, buf, len, flags);
waitlldbg("recv %d\n", ret);
cc3000_lib_unlock();
return ret;
}
/*****************************************************************************
* Name: recvfrom
*
* Description:
* read data from socket
* function receives a message from a connection-mode or
* connectionless-mode socket. Note that raw sockets are not
* supported.
*
* NOTE: On this version, only blocking mode is supported.
*
* Input Parameters:
* sockfd socket handle
* buf Points to the buffer where the message should be stored
* len Specifies the length in bytes of the buffer pointed to
* by the buffer argument.
* flags Specifies the type of message reception.
* On this version, this parameter is not supported.
* from pointer to an address structure indicating the source
* address: sockaddr. On this version only AF_INET is
* supported.
* fromlen source address tructure size
*
* Returned Value:
* Return the number of bytes received, or -1 if an error
* occurred
*
*****************************************************************************/
ssize_t cc3000_recvfrom(int sockfd, FAR void *buf, size_t len, int flags,
FAR struct sockaddr *from, FAR socklen_t *fromlen)
{
ssize_t ret;
#ifdef CONFIG_CC3000_MT
ret = cc3000_wait_data(sockfd);
if (ret == -ECONNABORTED)
{
return 0;
}
if (ret != OK)
{
return -1;
}
#endif
cc3000_lib_lock();
ret = cc3000_recvfrom_impl(sockfd, buf, len, flags, from, fromlen);
cc3000_lib_unlock();
return ret;
}
/*****************************************************************************
* Name: send
*
* Description:
* Write data to TCP socket
* This function is used to transmit a message to another
* socket.
*
* NOTE: On this version, only blocking mode is supported.
*
* Input Parameters:
* sockfd socket handle
* buf Points to a buffer containing the message to be sent
* len message size in bytes
* flags On this version, this parameter is not supported
*
* Returned Value:
* Return the number of bytes transmitted, or -1 if an
* error occurred
*
*****************************************************************************/
ssize_t cc3000_send(int sockfd, FAR const void *buf, size_t len, int flags)
{
ssize_t ret;
cc3000_lib_lock();
ret = cc3000_send_impl(sockfd, buf, len, flags);
cc3000_lib_unlock();
return ret;
}
/*****************************************************************************
* Name: sendto
*
* Description:
* Write data to TCP socket
* This function is used to transmit a message to another
* socket.
*
* NOTE: On this version, only blocking mode is supported.
*
* Input Parameters:
* sockfd socket handle
* buf Points to a buffer containing the message to be sent
* len message size in bytes
* flags On this version, this parameter is not supported
* to pointer to an address structure indicating the destination
* address: sockaddr. On this version only AF_INET is
* supported.
* tolen destination address structure size
*
* Returned Value:
* Return the number of bytes transmitted, or -1 if an
* error occurred
*
*****************************************************************************/
ssize_t cc3000_sendto(int sockfd, FAR const void *buf, size_t len, int flags,
FAR const struct sockaddr *to, socklen_t tolen)
{
ssize_t ret;
cc3000_lib_lock();
ret = cc3000_sendto_impl(sockfd, buf, len, flags, to, tolen);
cc3000_lib_unlock();
return ret;
}
/*****************************************************************************
* Name: gethostbyname
*
* Description:
* Get host IP by name. Obtain the IP Address of machine on network,
* by its name.
*
* NOTE: On this version, only blocking mode is supported. Also note that
* the function requires DNS server to be configured prior to its
* usage.
*
* Input Parameters:
* hostname host name
* usNameLen name length
* out_ip_addr This parameter is filled in with host IP address.
* In case that host name is not resolved,
* out_ip_addr is zero.
*
* Returned Value:
* On success, positive is returned. On error, negative is returned
*
*****************************************************************************/
#ifndef CC3000_TINY_DRIVER
// TODO: Standard is struct hostent *gethostbyname(const char *name);
int cc3000_gethostbyname(char * hostname, uint16_t usNameLen, unsigned long* out_ip_addr)
{
int ret;
cc3000_lib_lock();
ret = cc3000_gethostbyname_impl(hostname, usNameLen, out_ip_addr);
cc3000_lib_unlock();
return ret;
}
#endif
/*****************************************************************************
* Name: mdnsAdvertiser
*
* Description:
* Set CC3000 in mDNS advertiser mode in order to advertise itself.
*
* Input Parameters:
* mdnsEnabled flag to enable/disable the mDNS feature
* deviceServiceName Service name as part of the published
* canonical domain name
* deviceServiceNameLength Length of the service name
*
* Returned Value:
* On success, zero is returned, return SOC_ERROR if socket was not
* opened successfully, or if an error occurred.
*
*****************************************************************************/
int cc3000_mdnsadvertiser(uint16_t mdnsEnabled, char *deviceServiceName,
uint16_t deviceServiceNameLength)
{
int ret;
cc3000_lib_lock();
ret = cc3000_mdnsadvertiser_impl(mdnsEnabled, deviceServiceName, deviceServiceNameLength);
cc3000_lib_unlock();
return ret;
}