| /** | 
 |  * Copyright (c) 2018 Linaro Limited. | 
 |  * | 
 |  * SPDX-License-Identifier: Apache-2.0 | 
 |  */ | 
 |  | 
 | #include "simplelink_log.h" | 
 | LOG_MODULE_DECLARE(LOG_MODULE_NAME); | 
 |  | 
 | #include <stdlib.h> | 
 | #include <limits.h> | 
 | #include <fcntl.h> | 
 |  | 
 | #include <zephyr.h> | 
 | /* Define sockaddr, etc, before simplelink.h */ | 
 | #include <net/socket_offload.h> | 
 |  | 
 | #include <errno.h> | 
 | #include <ti/drivers/net/wifi/simplelink.h> | 
 | #include <ti/drivers/net/wifi/source/driver.h> | 
 | #include <ti/net/slnetutils.h> | 
 | #include <ti/net/slnetif.h> | 
 | #include "simplelink_support.h" | 
 |  | 
 | #include "sockets_internal.h" | 
 | #include "tls_internal.h" | 
 |  | 
 | #define FAILED (-1) | 
 |  | 
 | /* Increment by 1 to make sure we do not store the value of 0, which has | 
 |  * a special meaning in the fdtable subsys. | 
 |  */ | 
 | #define SD_TO_OBJ(sd) ((void *)(sd + 1)) | 
 | #define OBJ_TO_SD(obj) (((int)obj) - 1) | 
 |  | 
 | static int simplelink_socket_accept(void *obj, struct sockaddr *addr, | 
 | 			     socklen_t *addrlen); | 
 |  | 
 | /* | 
 |  * Convert SL error codes into BSD errno values | 
 |  * note that we are handling the same set of values as in TI SlNetSock | 
 |  * minus the ones that are not defined in ti/drivers/net/wifi/errors.h. | 
 |  */ | 
 | static int getErrno(_i32 error) | 
 | { | 
 | 	if (error >= 0) { | 
 | 		return error; | 
 | 	} | 
 | 	/* This switch case block is necessary for translating the NWP error | 
 | 	 * code to BSD ones. The #ifdef in each case are made in order to | 
 | 	 * reduce code footprint: These cases are compiled if and only if | 
 | 	 * there's a discrepancy between the BSD error number and the error | 
 | 	 * code returned by the NWP. | 
 | 	 */ | 
 | 	switch (error) { | 
 | #if EBADF != SL_ERROR_BSD_EBADF | 
 | 	case SL_ERROR_BSD_EBADF: | 
 | 		error = EBADF; | 
 | 		break; | 
 | #endif | 
 | #if ENSOCK !=  SL_ERROR_BSD_ENSOCK | 
 | 	case SL_ERROR_BSD_ENSOCK: | 
 | 		/* The limit on total # of open sockets has been reached */ | 
 | 		error = ENSOCK; | 
 | 		break; | 
 | #endif | 
 | #if EAGAIN != SL_ERROR_BSD_EAGAIN | 
 | 	case SL_ERROR_BSD_EAGAIN: | 
 | 		error = EAGAIN; | 
 | 		break; | 
 | #endif | 
 | #if ENOMEM != SL_ERROR_BSD_ENOMEM | 
 | 	case SL_ERROR_BSD_ENOMEM: | 
 | 		error = ENOMEM; | 
 | 		break; | 
 | #endif | 
 | #if EACCES != SL_ERROR_BSD_EACCES | 
 | 	case SL_ERROR_BSD_EACCES: | 
 | 		error = EACCES; | 
 | 		break; | 
 | #endif | 
 | #if EFAULT != SL_ERROR_BSD_EFAULT | 
 | 	case SL_ERROR_BSD_EFAULT: | 
 | 		error = EFAULT; | 
 | 		break; | 
 | #endif | 
 | #if EINVAL != SL_ERROR_BSD_EINVAL | 
 | 	case SL_ERROR_BSD_EINVAL: | 
 | 		error = EINVAL; | 
 | 		break; | 
 | #endif | 
 | #if EDESTADDRREQ != SL_ERROR_BSD_EDESTADDRREQ | 
 | 	case SL_ERROR_BSD_EDESTADDRREQ: | 
 | 		error = EDESTADDRREQ; | 
 | 		break; | 
 | #endif | 
 | #if EPROTOTYPE != SL_ERROR_BSD_EPROTOTYPE | 
 | 	case SL_ERROR_BSD_EPROTOTYPE: | 
 | 		error = EPROTOTYPE; | 
 | 		break; | 
 | #endif | 
 | #if ENOPROTOOPT != SL_ERROR_BSD_ENOPROTOOPT | 
 | 	case SL_ERROR_BSD_ENOPROTOOPT: | 
 | 		error = ENOPROTOOPT; | 
 | 		break; | 
 | #endif | 
 | #if EPROTONOSUPPORT != SL_ERROR_BSD_EPROTONOSUPPORT | 
 | 	case SL_ERROR_BSD_EPROTONOSUPPORT: | 
 | 		error = EPROTONOSUPPORT; | 
 | 		break; | 
 | #endif | 
 | #if EOPNOTSUPP != SL_ERROR_BSD_EOPNOTSUPP | 
 | 	case SL_ERROR_BSD_EOPNOTSUPP: | 
 | 		error = EOPNOTSUPP; | 
 | 		break; | 
 | #endif | 
 | #if EAFNOSUPPORT != SL_ERROR_BSD_EAFNOSUPPORT | 
 | 	case SL_ERROR_BSD_EAFNOSUPPORT: | 
 | 		error = EAFNOSUPPORT; | 
 | 		break; | 
 | #endif | 
 | #if EADDRINUSE != SL_ERROR_BSD_EADDRINUSE | 
 | 	case SL_ERROR_BSD_EADDRINUSE: | 
 | 		error = EADDRINUSE; | 
 | 		break; | 
 | #endif | 
 | #if EADDRNOTAVAIL != SL_ERROR_BSD_EADDRNOTAVAIL | 
 | 	case SL_ERROR_BSD_EADDRNOTAVAIL: | 
 | 		error = EADDRNOTAVAIL; | 
 | 		break; | 
 | #endif | 
 | #if ENETUNREACH != SL_ERROR_BSD_ENETUNREACH | 
 | 	case SL_ERROR_BSD_ENETUNREACH: | 
 | 		error = ENETUNREACH; | 
 | 		break; | 
 | #endif | 
 | #if ENOBUFS != SL_ERROR_BSD_ENOBUFS | 
 | 	case SL_ERROR_BSD_ENOBUFS: | 
 | 		error = ENOBUFS; | 
 | 		break; | 
 | #endif | 
 | #if EISCONN != SL_ERROR_BSD_EISCONN | 
 | 	case SL_ERROR_BSD_EISCONN: | 
 | 		error = EISCONN; | 
 | 		break; | 
 | #endif | 
 | #if ENOTCONN != SL_ERROR_BSD_ENOTCONN | 
 | 	case SL_ERROR_BSD_ENOTCONN: | 
 | 		error = ENOTCONN; | 
 | 		break; | 
 | #endif | 
 | #if ETIMEDOUT != SL_ERROR_BSD_ETIMEDOUT | 
 | 	case SL_ERROR_BSD_ETIMEDOUT: | 
 | 		error = ETIMEDOUT; | 
 | 		break; | 
 | #endif | 
 | #if ECONNREFUSED != SL_ERROR_BSD_ECONNREFUSED | 
 | 	case SL_ERROR_BSD_ECONNREFUSED: | 
 | 		error = ECONNREFUSED; | 
 | 		break; | 
 | #endif | 
 | 	/* The cases below are proprietary driver errors, which can | 
 | 	 * be returned by the SimpleLink Driver, in various cases of failure. | 
 | 	 * Each is mapped to the corresponding BSD error. | 
 | 	 */ | 
 | 	case SL_POOL_IS_EMPTY: | 
 | 	case SL_RET_CODE_NO_FREE_ASYNC_BUFFERS_ERROR: | 
 | 	case SL_RET_CODE_MALLOC_ERROR: | 
 | 		error = ENOMEM; | 
 | 		break; | 
 | 	case SL_RET_CODE_INVALID_INPUT: | 
 | 	case SL_EZEROLEN: | 
 | 	case SL_ESMALLBUF: | 
 | 	case SL_INVALPARAM: | 
 | 		error = EINVAL; | 
 | 		break; | 
 | 	default: | 
 | 	/* Do nothing .. | 
 | 	 * If no case is true, that means that the BSD error | 
 | 	 * code and the code returned by the NWP are either identical, | 
 | 	 * or no proprietary error has occurred. | 
 | 	 */ | 
 | 		break; | 
 | 	} | 
 |  | 
 | 	return error; | 
 | } | 
 |  | 
 | static int simplelink_socket(int family, int type, int proto) | 
 | { | 
 | 	uint8_t sec_method = SL_SO_SEC_METHOD_SSLv3_TLSV1_2; | 
 | 	int sd; | 
 | 	int retval = 0; | 
 | 	int sl_proto = proto; | 
 |  | 
 | 	/* Map Zephyr socket.h family to SimpleLink's: */ | 
 | 	switch (family) { | 
 | 	case AF_INET: | 
 | 		family = SL_AF_INET; | 
 | 		break; | 
 | 	case AF_INET6: | 
 | 		family = SL_AF_INET6; | 
 | 		break; | 
 | 	default: | 
 | 		LOG_ERR("unsupported family: %d", family); | 
 | 		retval = slcb_SetErrno(EAFNOSUPPORT); | 
 | 		goto exit; | 
 | 	} | 
 |  | 
 | 	/* Map Zephyr socket.h type to SimpleLink's: */ | 
 | 	switch (type) { | 
 | 	case SOCK_STREAM: | 
 | 		type = SL_SOCK_STREAM; | 
 | 		break; | 
 | 	case SOCK_DGRAM: | 
 | 		type = SL_SOCK_DGRAM; | 
 | 		break; | 
 | 	case SOCK_RAW: | 
 | 		type = SL_SOCK_RAW; | 
 | 		break; | 
 | 	default: | 
 | 		LOG_ERR("unrecognized type: %d", type); | 
 | 		retval = slcb_SetErrno(ESOCKTNOSUPPORT); | 
 | 		goto exit; | 
 | 	} | 
 |  | 
 | 	/* Map Zephyr protocols to TI's values: */ | 
 | 	if (proto >= IPPROTO_TLS_1_0 && proto <= IPPROTO_TLS_1_2) { | 
 | 		sl_proto = SL_SEC_SOCKET; | 
 | 	} else if (proto >= IPPROTO_DTLS_1_0 && proto <= IPPROTO_DTLS_1_2) { | 
 | 		/* SimpleLink doesn't handle DTLS yet! */ | 
 | 		retval = slcb_SetErrno(EPROTONOSUPPORT); | 
 | 		goto exit; | 
 | 	} else { | 
 | 		switch (proto) { | 
 | 		case IPPROTO_TCP: | 
 | 			sl_proto = SL_IPPROTO_TCP; | 
 | 			break; | 
 | 		case IPPROTO_UDP: | 
 | 			sl_proto = SL_IPPROTO_UDP; | 
 | 			break; | 
 | 		default: | 
 | 			LOG_ERR("unrecognized proto: %d", sl_proto); | 
 | 			retval = slcb_SetErrno(EPROTONOSUPPORT); | 
 | 			goto exit; | 
 | 		} | 
 | 	} | 
 |  | 
 | 	sd = sl_Socket(family, type, sl_proto); | 
 | 	if (sd >= 0) { | 
 | 		if (IS_ENABLED(CONFIG_NET_SOCKETS_SOCKOPT_TLS) | 
 | 		    && sl_proto == SL_SEC_SOCKET) { | 
 | 			/* Now, set specific TLS version via setsockopt(): */ | 
 | 			sec_method = (proto - IPPROTO_TLS_1_0) + | 
 | 				SL_SO_SEC_METHOD_TLSV1; | 
 | 			retval = sl_SetSockOpt(sd, SL_SOL_SOCKET, | 
 | 				SL_SO_SECMETHOD, | 
 | 				&sec_method, sizeof(sec_method)); | 
 | 			if (retval < 0) { | 
 | 				retval = slcb_SetErrno(EPROTONOSUPPORT); | 
 | 				(void)sl_Close(sd); | 
 | 				goto exit; | 
 | 			} | 
 | 		} | 
 | 	} | 
 |  | 
 | 	retval = sd; | 
 |  | 
 | 	if (retval < 0) { | 
 | 		retval = slcb_SetErrno(getErrno(retval)); | 
 | 	} | 
 |  | 
 | exit: | 
 | 	return retval; | 
 | } | 
 |  | 
 | static int simplelink_close(void *obj) | 
 | { | 
 | 	int sd = OBJ_TO_SD(obj); | 
 | 	int retval; | 
 |  | 
 | 	retval = sl_Close(sd); | 
 |  | 
 | 	if (retval < 0) { | 
 | 		retval = slcb_SetErrno(getErrno(retval)); | 
 | 	} | 
 |  | 
 | 	return retval; | 
 | } | 
 |  | 
 | static SlSockAddr_t *translate_z_to_sl_addrlen(socklen_t addrlen, | 
 | 					       SlSockAddrIn_t *sl_addr_in, | 
 | 					       SlSockAddrIn6_t *sl_addr_in6, | 
 | 					       SlSocklen_t *sl_addrlen) | 
 | { | 
 | 	SlSockAddr_t *sl_addr = NULL; | 
 |  | 
 | 	if (addrlen == sizeof(struct sockaddr_in)) { | 
 | 		*sl_addrlen = sizeof(SlSockAddrIn_t); | 
 | 		sl_addr = (SlSockAddr_t *)sl_addr_in; | 
 | 	} else if (addrlen == sizeof(struct sockaddr_in6)) { | 
 | 		*sl_addrlen = sizeof(SlSockAddrIn6_t); | 
 | 		sl_addr = (SlSockAddr_t *)sl_addr_in6; | 
 | 	} | 
 |  | 
 | 	return sl_addr; | 
 | } | 
 |  | 
 | static SlSockAddr_t *translate_z_to_sl_addrs(const struct sockaddr *addr, | 
 | 					     socklen_t addrlen, | 
 | 					     SlSockAddrIn_t *sl_addr_in, | 
 | 					     SlSockAddrIn6_t *sl_addr_in6, | 
 | 					     SlSocklen_t *sl_addrlen) | 
 | { | 
 | 	SlSockAddr_t *sl_addr = NULL; | 
 |  | 
 | 	if (addrlen == sizeof(struct sockaddr_in)) { | 
 | 		struct sockaddr_in *z_sockaddr_in = (struct sockaddr_in *)addr; | 
 |  | 
 | 		*sl_addrlen = sizeof(SlSockAddrIn_t); | 
 | 		sl_addr_in->sin_family = SL_AF_INET; | 
 | 		sl_addr_in->sin_port = z_sockaddr_in->sin_port; | 
 | 		sl_addr_in->sin_addr.s_addr = | 
 | 			z_sockaddr_in->sin_addr.s_addr; | 
 |  | 
 | 		sl_addr = (SlSockAddr_t *)sl_addr_in; | 
 | 	} else if (addrlen == sizeof(struct sockaddr_in6)) { | 
 | 		struct sockaddr_in6 *z_sockaddr_in6 = | 
 | 			(struct sockaddr_in6 *)addr; | 
 |  | 
 | 		*sl_addrlen = sizeof(SlSockAddrIn6_t); | 
 | 		sl_addr_in6->sin6_family = SL_AF_INET6; | 
 | 		sl_addr_in6->sin6_port = z_sockaddr_in6->sin6_port; | 
 | 		memcpy(sl_addr_in6->sin6_addr._S6_un._S6_u32, | 
 | 		       z_sockaddr_in6->sin6_addr.s6_addr, | 
 | 		       sizeof(sl_addr_in6->sin6_addr._S6_un._S6_u32)); | 
 |  | 
 | 		sl_addr = (SlSockAddr_t *)sl_addr_in6; | 
 | 	} | 
 |  | 
 | 	return sl_addr; | 
 | } | 
 |  | 
 | static void translate_sl_to_z_addr(SlSockAddr_t *sl_addr, | 
 | 				   SlSocklen_t sl_addrlen, | 
 | 				   struct sockaddr *addr, | 
 | 				   socklen_t *addrlen) | 
 | { | 
 | 	SlSockAddrIn_t *sl_addr_in; | 
 | 	SlSockAddrIn6_t *sl_addr_in6; | 
 |  | 
 | 	if (sl_addr->sa_family == SL_AF_INET) { | 
 | 		if (sl_addrlen == (SlSocklen_t)sizeof(SlSockAddrIn_t)) { | 
 | 			struct sockaddr_in *z_sockaddr_in = | 
 | 				(struct sockaddr_in *)addr; | 
 |  | 
 | 			sl_addr_in = (SlSockAddrIn_t *)sl_addr; | 
 | 			z_sockaddr_in->sin_family = AF_INET; | 
 | 			z_sockaddr_in->sin_port = sl_addr_in->sin_port; | 
 | 			z_sockaddr_in->sin_addr.s_addr = | 
 | 				sl_addr_in->sin_addr.s_addr; | 
 | 			*addrlen = sizeof(struct sockaddr_in); | 
 | 		} else { | 
 | 			*addrlen = sl_addrlen; | 
 | 		} | 
 | 	} else if (sl_addr->sa_family == SL_AF_INET6) { | 
 | 		if (sl_addrlen == sizeof(SlSockAddrIn6_t)) { | 
 | 			struct sockaddr_in6 *z_sockaddr_in6 = | 
 | 				(struct sockaddr_in6 *)addr; | 
 | 			sl_addr_in6 = (SlSockAddrIn6_t *)sl_addr; | 
 |  | 
 | 			z_sockaddr_in6->sin6_family = AF_INET6; | 
 | 			z_sockaddr_in6->sin6_port = sl_addr_in6->sin6_port; | 
 | 			z_sockaddr_in6->sin6_scope_id = | 
 | 				(uint8_t)sl_addr_in6->sin6_scope_id; | 
 | 			memcpy(z_sockaddr_in6->sin6_addr.s6_addr, | 
 | 			       sl_addr_in6->sin6_addr._S6_un._S6_u32, | 
 | 			       sizeof(z_sockaddr_in6->sin6_addr.s6_addr)); | 
 | 			*addrlen = sizeof(struct sockaddr_in6); | 
 | 		} else { | 
 | 			*addrlen = sl_addrlen; | 
 | 		} | 
 | 	} | 
 | } | 
 |  | 
 | static int simplelink_accept(void *obj, struct sockaddr *addr, | 
 | 			     socklen_t *addrlen) | 
 | { | 
 | 	int sd = OBJ_TO_SD(obj); | 
 | 	int retval; | 
 | 	SlSockAddr_t *sl_addr; | 
 | 	SlSockAddrIn_t sl_addr_in; | 
 | 	SlSockAddrIn6_t sl_addr_in6; | 
 | 	SlSocklen_t sl_addrlen; | 
 |  | 
 | 	if ((addrlen == NULL) || (addr == NULL)) { | 
 | 		retval = SL_RET_CODE_INVALID_INPUT; | 
 | 		goto exit; | 
 | 	} | 
 |  | 
 | 	/* Translate between Zephyr's and SimpleLink's sockaddr's: */ | 
 | 	sl_addr = translate_z_to_sl_addrlen(*addrlen, &sl_addr_in, &sl_addr_in6, | 
 | 				  &sl_addrlen); | 
 | 	if (sl_addr == NULL) { | 
 | 		retval = SL_RET_CODE_INVALID_INPUT; | 
 | 		goto exit; | 
 | 	} | 
 |  | 
 | 	retval = sl_Accept(sd, sl_addr, &sl_addrlen); | 
 | 	if (retval < 0) { | 
 | 		goto exit; | 
 | 	} | 
 |  | 
 | 	/* Translate returned sl_addr into *addr and set *addrlen: */ | 
 | 	translate_sl_to_z_addr(sl_addr, sl_addrlen, addr, addrlen); | 
 |  | 
 | exit: | 
 | 	if (retval < 0) { | 
 | 		retval = slcb_SetErrno(getErrno(retval)); | 
 | 	} | 
 |  | 
 | 	return retval; | 
 | } | 
 |  | 
 | static int simplelink_bind(void *obj, const struct sockaddr *addr, | 
 | 			   socklen_t addrlen) | 
 | { | 
 | 	int sd = OBJ_TO_SD(obj); | 
 | 	int retval; | 
 | 	SlSockAddr_t *sl_addr; | 
 | 	SlSockAddrIn_t sl_addr_in; | 
 | 	SlSockAddrIn6_t sl_addr_in6; | 
 | 	SlSocklen_t sl_addrlen; | 
 |  | 
 | 	if (addr == NULL) { | 
 | 		retval = slcb_SetErrno(EISDIR); | 
 | 		return retval; | 
 | 	} | 
 |  | 
 | 	/* Translate to sl_Bind() parameters: */ | 
 | 	sl_addr = translate_z_to_sl_addrs(addr, addrlen, &sl_addr_in, | 
 | 					  &sl_addr_in6, &sl_addrlen); | 
 |  | 
 | 	if (sl_addr == NULL) { | 
 | 		retval = SL_RET_CODE_INVALID_INPUT; | 
 | 		goto exit; | 
 | 	} | 
 |  | 
 | 	retval = sl_Bind(sd, sl_addr, sl_addrlen); | 
 |  | 
 | exit: | 
 | 	if (retval < 0) { | 
 | 		retval = slcb_SetErrno(getErrno(retval)); | 
 | 	} | 
 |  | 
 | 	return retval; | 
 | } | 
 |  | 
 | static int simplelink_listen(void *obj, int backlog) | 
 | { | 
 | 	int sd = OBJ_TO_SD(obj); | 
 | 	int retval; | 
 |  | 
 | 	retval = (int)sl_Listen(sd, backlog); | 
 |  | 
 | 	if (retval < 0) { | 
 | 		retval = slcb_SetErrno(getErrno(retval)); | 
 | 	} | 
 |  | 
 | 	return retval; | 
 | } | 
 |  | 
 | static int simplelink_connect(void *obj, const struct sockaddr *addr, | 
 | 			      socklen_t addrlen) | 
 | { | 
 | 	int sd = OBJ_TO_SD(obj); | 
 | 	int retval; | 
 | 	SlSockAddr_t *sl_addr; | 
 | 	SlSockAddrIn_t sl_addr_in; | 
 | 	SlSockAddrIn6_t sl_addr_in6; | 
 | 	SlSocklen_t sl_addrlen; | 
 |  | 
 | 	__ASSERT_NO_MSG(addr); | 
 |  | 
 | 	/* Translate to sl_Connect() parameters: */ | 
 | 	sl_addr = translate_z_to_sl_addrs(addr, addrlen, &sl_addr_in, | 
 | 					  &sl_addr_in6, &sl_addrlen); | 
 |  | 
 | 	if (sl_addr == NULL) { | 
 | 		retval = SL_RET_CODE_INVALID_INPUT; | 
 | 		goto exit; | 
 | 	} | 
 |  | 
 | 	retval = sl_Connect(sd, sl_addr, sl_addrlen); | 
 |  | 
 | 	/* TBD: Until we have a good way to get correct date from Zephyr, | 
 | 	 * log a date validation error as a warning, but continue connection: | 
 | 	 */ | 
 | 	if (retval == SL_ERROR_BSD_ESECDATEERROR) { | 
 | 		LOG_WRN("Failed certificate date validation: %d", retval); | 
 | 		retval = 0; | 
 | 	} | 
 |  | 
 | 	/* Warn users when root CA is not in the certificate catalog. | 
 | 	 * For enhanced security, users should update the catalog with the | 
 | 	 * certificates for sites the device is expected to connect to. Note | 
 | 	 * the connection is established successfully even when the root CA | 
 | 	 * is not part of the catalog. | 
 | 	 */ | 
 | 	if (retval == SL_ERROR_BSD_ESECUNKNOWNROOTCA) { | 
 | 		LOG_WRN("Unknown root CA used. For proper security, please " | 
 | 			"use a root CA that is part of the certificate " | 
 | 			"catalog in production systems."); | 
 | 		retval = 0; | 
 | 	} | 
 |  | 
 | exit: | 
 | 	if (retval < 0) { | 
 | 		retval = slcb_SetErrno(getErrno(retval)); | 
 | 	} | 
 |  | 
 | 	return retval; | 
 | } | 
 |  | 
 | #define ONE_THOUSAND 1000 | 
 |  | 
 | static const struct socket_op_vtable simplelink_socket_fd_op_vtable; | 
 |  | 
 | static int simplelink_poll(struct zsock_pollfd *fds, int nfds, int msecs) | 
 | { | 
 | 	int max_sd = 0; | 
 | 	struct SlTimeval_t tv, *ptv; | 
 | 	SlFdSet_t rfds;	 /* Set of read file descriptors */ | 
 | 	SlFdSet_t wfds;	 /* Set of write file descriptors */ | 
 | 	int i, retval, sd; | 
 | 	void *obj; | 
 |  | 
 | 	if (nfds > SL_FD_SETSIZE) { | 
 | 		retval = slcb_SetErrno(EINVAL); | 
 | 		goto exit; | 
 | 	} | 
 |  | 
 | 	/* Convert time to SlTimeval struct values: */ | 
 | 	if (msecs == SYS_FOREVER_MS) { | 
 | 		ptv = NULL; | 
 | 	} else { | 
 | 		tv.tv_sec = msecs / ONE_THOUSAND; | 
 | 		tv.tv_usec = (msecs % ONE_THOUSAND) * ONE_THOUSAND; | 
 | 		ptv = &tv; | 
 | 	} | 
 |  | 
 | 	/* Setup read and write fds for select, based on pollfd fields: */ | 
 | 	SL_SOCKET_FD_ZERO(&rfds); | 
 | 	SL_SOCKET_FD_ZERO(&wfds); | 
 |  | 
 | 	for (i = 0; i < nfds; i++) { | 
 | 		fds[i].revents = 0; | 
 | 		if (fds[i].fd < 0) { | 
 | 			continue; | 
 | 		} else { | 
 | 			obj = z_get_fd_obj(fds[i].fd, | 
 | 					   (const struct fd_op_vtable *) | 
 | 						&simplelink_socket_fd_op_vtable, | 
 | 					   ENOTSUP); | 
 | 			if (obj != NULL) { | 
 | 				/* Offloaded socket found. */ | 
 | 				sd = OBJ_TO_SD(obj); | 
 | 			} else { | 
 | 				/* Non-offloaded socket, return an error. */ | 
 | 				retval = slcb_SetErrno(EINVAL); | 
 | 				goto exit; | 
 | 			} | 
 | 		} | 
 | 		if (fds[i].events & ZSOCK_POLLIN) { | 
 | 			SL_SOCKET_FD_SET(sd, &rfds); | 
 | 		} | 
 | 		if (fds[i].events & ZSOCK_POLLOUT) { | 
 | 			SL_SOCKET_FD_SET(sd, &wfds); | 
 | 		} | 
 | 		if (sd > max_sd) { | 
 | 			max_sd = sd; | 
 | 		} | 
 | 	} | 
 |  | 
 | 	/* Wait for requested read and write fds to be ready: */ | 
 | 	retval = sl_Select(max_sd + 1, &rfds, &wfds, NULL, ptv); | 
 | 	if (retval > 0) { | 
 | 		for (i = 0; i < nfds; i++) { | 
 | 			if (fds[i].fd >= 0) { | 
 | 				obj = z_get_fd_obj( | 
 | 					fds[i].fd, | 
 | 					(const struct fd_op_vtable *) | 
 | 						&simplelink_socket_fd_op_vtable, | 
 | 					ENOTSUP); | 
 | 				sd = OBJ_TO_SD(obj); | 
 | 				if (SL_SOCKET_FD_ISSET(sd, &rfds)) { | 
 | 					fds[i].revents |= ZSOCK_POLLIN; | 
 | 				} | 
 | 				if (SL_SOCKET_FD_ISSET(sd, &wfds)) { | 
 | 					fds[i].revents |= ZSOCK_POLLOUT; | 
 | 				} | 
 | 			} | 
 | 		} | 
 | 	} | 
 |  | 
 | 	if (retval < 0) { | 
 | 		retval = slcb_SetErrno(getErrno(retval)); | 
 | 	} | 
 |  | 
 | exit: | 
 | 	return retval; | 
 | } | 
 |  | 
 | #ifdef CONFIG_NET_SOCKETS_SOCKOPT_TLS | 
 |  | 
 | /* Iterate through the list of Zephyr's credential types, and | 
 |  * map to SimpleLink values, then set stored filenames | 
 |  * via SimpleLink's sl_SetSockOpt() | 
 |  */ | 
 | static int map_credentials(int sd, const void *optval, socklen_t optlen) | 
 | { | 
 | 	sec_tag_t *sec_tags = (sec_tag_t *)optval; | 
 | 	int retval = 0; | 
 | 	int sec_tags_len; | 
 | 	sec_tag_t tag; | 
 | 	int opt; | 
 | 	int i; | 
 | 	struct tls_credential *cert; | 
 |  | 
 | 	if ((optlen % sizeof(sec_tag_t)) != 0 || (optlen == 0)) { | 
 | 		retval = EINVAL; | 
 | 		goto exit; | 
 | 	} else { | 
 | 		sec_tags_len = optlen / sizeof(sec_tag_t); | 
 | 	} | 
 |  | 
 | 	/* For each tag, retrieve the credentials value and type: */ | 
 | 	for (i = 0; i < sec_tags_len; i++) { | 
 | 		tag = sec_tags[i]; | 
 | 		cert = credential_next_get(tag, NULL); | 
 | 		while (cert != NULL) { | 
 | 			/* Map Zephyr cert types to Simplelink cert options: */ | 
 | 			switch (cert->type) { | 
 | 			case TLS_CREDENTIAL_CA_CERTIFICATE: | 
 | 				opt = SL_SO_SECURE_FILES_CA_FILE_NAME; | 
 | 				break; | 
 | 			case TLS_CREDENTIAL_SERVER_CERTIFICATE: | 
 | 				opt = SL_SO_SECURE_FILES_CERTIFICATE_FILE_NAME; | 
 | 				break; | 
 | 			case TLS_CREDENTIAL_PRIVATE_KEY: | 
 | 				opt = SL_SO_SECURE_FILES_PRIVATE_KEY_FILE_NAME; | 
 | 				break; | 
 | 			case TLS_CREDENTIAL_NONE: | 
 | 			case TLS_CREDENTIAL_PSK: | 
 | 			case TLS_CREDENTIAL_PSK_ID: | 
 | 			default: | 
 | 				/* Not handled by SimpleLink: */ | 
 | 				retval = EINVAL; | 
 | 				goto exit; | 
 | 			} | 
 | 			retval = sl_SetSockOpt(sd, SL_SOL_SOCKET, opt, | 
 | 					       cert->buf, | 
 | 					       (SlSocklen_t)cert->len); | 
 | 			if (retval < 0) { | 
 | 				retval = getErrno(retval); | 
 | 				break; | 
 | 			} | 
 | 			cert = credential_next_get(tag, cert); | 
 | 		} | 
 | 	} | 
 |  | 
 | exit: | 
 | 	return retval; | 
 | } | 
 | #else | 
 | static int map_credentials(int sd, const void *optval, socklen_t optlen) | 
 | { | 
 | 	return 0; | 
 | } | 
 | #endif  /* CONFIG_NET_SOCKETS_SOCKOPT_TLS */ | 
 |  | 
 | /* Excerpted from SimpleLink's socket.h: | 
 |  * "Unsupported: these are only placeholders to not break BSD code." | 
 |  *  Remove once Zephyr has POSIX socket options defined. | 
 |  */ | 
 | #define SO_BROADCAST  (200) | 
 | #define SO_SNDBUF     (202) | 
 |  | 
 | /* Needed to keep line lengths < 80: */ | 
 | #define _SEC_DOMAIN_VERIF SL_SO_SECURE_DOMAIN_NAME_VERIFICATION | 
 |  | 
 | static int simplelink_setsockopt(void *obj, int level, int optname, | 
 | 				 const void *optval, socklen_t optlen) | 
 | { | 
 | 	int sd = OBJ_TO_SD(obj); | 
 | 	int retval; | 
 |  | 
 | 	if (IS_ENABLED(CONFIG_NET_SOCKETS_SOCKOPT_TLS) && level == SOL_TLS) { | 
 | 		/* Handle Zephyr's SOL_TLS secure socket options: */ | 
 | 		switch (optname) { | 
 | 		case TLS_SEC_TAG_LIST: | 
 | 			/* Bind credential filenames to this socket: */ | 
 | 			retval = map_credentials(sd, optval, optlen); | 
 | 			if (retval != 0) { | 
 | 				retval = slcb_SetErrno(retval); | 
 | 				goto exit; | 
 | 			} | 
 | 			break; | 
 | 		case TLS_HOSTNAME: | 
 | 			retval = sl_SetSockOpt(sd, SL_SOL_SOCKET, | 
 | 					       _SEC_DOMAIN_VERIF, | 
 | 					       (const char *)optval, optlen); | 
 | 			break; | 
 | 		case TLS_PEER_VERIFY: | 
 | 			if (optval) { | 
 | 				/* | 
 | 				 * Not currently supported. Verification | 
 | 				 * is automatically performed if a CA | 
 | 				 * certificate is set. We are returning | 
 | 				 * success here to allow | 
 | 				 * mqtt_client_tls_connect() | 
 | 				 * to proceed, given it requires | 
 | 				 * verification and it is indeed | 
 | 				 * performed when the cert is set. | 
 | 				 */ | 
 | 				if (*(uint32_t *)optval != 2U) { | 
 | 					retval = slcb_SetErrno(ENOTSUP); | 
 | 					goto exit; | 
 | 				} else { | 
 | 					retval = 0; | 
 | 				} | 
 | 			} else { | 
 | 				retval = slcb_SetErrno(EINVAL); | 
 | 				goto exit; | 
 | 			} | 
 | 			break; | 
 | 		case TLS_CIPHERSUITE_LIST: | 
 | 		case TLS_DTLS_ROLE: | 
 | 			/* Not yet supported: */ | 
 | 			retval = slcb_SetErrno(ENOTSUP); | 
 | 			goto exit; | 
 | 		default: | 
 | 			retval = slcb_SetErrno(EINVAL); | 
 | 			goto exit; | 
 | 		} | 
 | 	} else { | 
 | 		/* Can be SOL_SOCKET or TI specific: */ | 
 |  | 
 | 		/* Note: this logic should match SimpleLink SDK's socket.c: */ | 
 | 		switch (optname) { | 
 | 		case TCP_NODELAY: | 
 | 			if (optval) { | 
 | 				/* if user wishes to have TCP_NODELAY = FALSE, | 
 | 				 * we return EINVAL and fail in the cases below. | 
 | 				 */ | 
 | 				if (*(uint32_t *)optval) { | 
 | 					retval = 0; | 
 | 					goto exit; | 
 | 				} | 
 | 			} | 
 | 			/* These sock opts aren't supported by the cc32xx | 
 | 			 * network stack, so we ignore them and set errno to | 
 | 			 * EINVAL in order to not break "off-the-shelf" BSD | 
 | 			 * code. | 
 | 			 */ | 
 | 		case SO_BROADCAST: | 
 | 		case SO_REUSEADDR: | 
 | 		case SO_SNDBUF: | 
 | 			retval = slcb_SetErrno(EINVAL); | 
 | 			goto exit; | 
 | 		default: | 
 | 			break; | 
 | 		} | 
 | 		retval = sl_SetSockOpt(sd, SL_SOL_SOCKET, optname, optval, | 
 | 				       (SlSocklen_t)optlen); | 
 | 	} | 
 |  | 
 | 	if (retval < 0) { | 
 | 		retval = slcb_SetErrno(getErrno(retval)); | 
 | 	} | 
 |  | 
 | exit: | 
 | 	return retval; | 
 | } | 
 |  | 
 | static int simplelink_getsockopt(void *obj, int level, int optname, | 
 | 				 void *optval, socklen_t *optlen) | 
 | { | 
 | 	int sd = OBJ_TO_SD(obj); | 
 | 	int retval; | 
 |  | 
 | 	if (IS_ENABLED(CONFIG_NET_SOCKETS_SOCKOPT_TLS) && level == SOL_TLS) { | 
 | 		/* Handle Zephyr's SOL_TLS secure socket options: */ | 
 | 		switch (optname) { | 
 | 		case TLS_SEC_TAG_LIST: | 
 | 		case TLS_CIPHERSUITE_LIST: | 
 | 		case TLS_CIPHERSUITE_USED: | 
 | 			/* Not yet supported: */ | 
 | 			retval = slcb_SetErrno(ENOTSUP); | 
 | 			goto exit; | 
 | 		default: | 
 | 			retval = slcb_SetErrno(EINVAL); | 
 | 			goto exit; | 
 | 		} | 
 | 	} else { | 
 | 		/* Can be SOL_SOCKET or TI specific: */ | 
 |  | 
 | 		/* Note: this logic should match SimpleLink SDK's socket.c: */ | 
 | 		switch (optname) { | 
 | 			/* TCP_NODELAY always set by the NWP, so return True */ | 
 | 		case TCP_NODELAY: | 
 | 			if (optval) { | 
 | 				(*(_u32 *)optval) = TRUE; | 
 | 				retval = 0; | 
 | 				goto exit; | 
 | 			} | 
 | 			/* These sock opts aren't supported by the cc32xx | 
 | 			 * network stack, so we silently ignore them and set | 
 | 			 * errno to EINVAL in order to not break "off-the-shelf" | 
 | 			 * BSD code. | 
 | 			 */ | 
 | 		case SO_BROADCAST: | 
 | 		case SO_REUSEADDR: | 
 | 		case SO_SNDBUF: | 
 | 			retval = slcb_SetErrno(EINVAL); | 
 | 			goto exit; | 
 | 		default: | 
 | 			break; | 
 | 		} | 
 | 		retval = sl_GetSockOpt(sd, SL_SOL_SOCKET, optname, optval, | 
 | 				       (SlSocklen_t *)optlen); | 
 | 	} | 
 |  | 
 | 	if (retval < 0) { | 
 | 		retval = slcb_SetErrno(getErrno(retval)); | 
 | 	} | 
 |  | 
 | exit: | 
 | 	return retval; | 
 | } | 
 |  | 
 | /* SimpleLink does not support flags in recv. | 
 |  * However, to enable more Zephyr apps to use this socket_offload, rather than | 
 |  * failing with ENOTSUP, we can closely emulate the MSG_DONTWAIT feature using | 
 |  * SimpleLink socket options. | 
 |  */ | 
 | static int handle_recv_flags(int sd, int flags, bool set, int *nb_enabled) | 
 | { | 
 | 	ssize_t retval = 0; | 
 | 	SlSocklen_t optlen = sizeof(SlSockNonblocking_t); | 
 | 	SlSockNonblocking_t enableOption; | 
 |  | 
 | 	if (flags & ZSOCK_MSG_PEEK) { | 
 | 		retval = ENOTSUP; | 
 | 	} else if (flags & ZSOCK_MSG_DONTWAIT) { | 
 | 		if (set) { | 
 | 			/* Get previous state, to restore later: */ | 
 | 			sl_GetSockOpt(sd, SL_SOL_SOCKET, SL_SO_NONBLOCKING, | 
 | 				      (_u8 *)&enableOption, &optlen); | 
 | 			*nb_enabled = enableOption.NonBlockingEnabled; | 
 |  | 
 | 			/* Now, set to non_blocking if not already set: */ | 
 | 			if (!*nb_enabled) { | 
 | 				enableOption.NonBlockingEnabled = 1; | 
 | 				sl_SetSockOpt(sd, SL_SOL_SOCKET, | 
 | 					      SL_SO_NONBLOCKING, | 
 | 					      (_u8 *)&enableOption, | 
 | 					      sizeof(enableOption)); | 
 | 			} | 
 | 		} else { | 
 | 			/* Restore socket to previous state: */ | 
 | 			enableOption.NonBlockingEnabled = *nb_enabled; | 
 | 			sl_SetSockOpt(sd, SL_SOL_SOCKET, SL_SO_NONBLOCKING, | 
 | 				      (_u8 *)&enableOption, | 
 | 				      sizeof(enableOption)); | 
 | 		} | 
 | 	} | 
 |  | 
 | 	return retval; | 
 | } | 
 |  | 
 | static ssize_t simplelink_recvfrom(void *obj, void *buf, size_t len, int flags, | 
 | 				   struct sockaddr *from, socklen_t *fromlen) | 
 | { | 
 | 	int sd = OBJ_TO_SD(obj); | 
 | 	ssize_t retval; | 
 | 	SlSockAddr_t *sl_addr; | 
 | 	SlSockAddrIn_t sl_addr_in; | 
 | 	SlSockAddrIn6_t sl_addr_in6; | 
 | 	SlSocklen_t sl_addrlen; | 
 | 	int nb_enabled; | 
 |  | 
 | 	retval = handle_recv_flags(sd, flags, TRUE, &nb_enabled); | 
 |  | 
 | 	if (!retval) { | 
 | 		/* Translate to sl_RecvFrom() parameters: */ | 
 | 		if (fromlen != NULL) { | 
 | 			sl_addr = translate_z_to_sl_addrlen(*fromlen, | 
 | 							    &sl_addr_in, | 
 | 							    &sl_addr_in6, | 
 | 							    &sl_addrlen); | 
 | 			retval = (ssize_t)sl_RecvFrom(sd, buf, len, 0, sl_addr, | 
 | 						      &sl_addrlen); | 
 | 		} else { | 
 | 			retval = (ssize_t)sl_Recv(sd, buf, len, 0); | 
 | 		} | 
 |  | 
 | 		handle_recv_flags(sd, flags, FALSE, &nb_enabled); | 
 | 		if (retval >= 0) { | 
 | 			if (fromlen != NULL) { | 
 | 				/* | 
 | 				 * Translate sl_addr into *addr and set | 
 | 				 * *addrlen | 
 | 				 */ | 
 | 				translate_sl_to_z_addr(sl_addr, sl_addrlen, | 
 | 						       from, fromlen); | 
 | 			} | 
 | 		} else { | 
 | 			retval = slcb_SetErrno(getErrno(retval)); | 
 | 		} | 
 | 	} else { | 
 | 		retval = slcb_SetErrno(retval); | 
 | 	} | 
 |  | 
 | 	return retval; | 
 | } | 
 |  | 
 | static ssize_t simplelink_sendto(void *obj, const void *buf, size_t len, | 
 | 				 int flags, const struct sockaddr *to, | 
 | 				 socklen_t tolen) | 
 | { | 
 | 	int sd = OBJ_TO_SD(obj); | 
 | 	ssize_t retval; | 
 | 	SlSockAddr_t *sl_addr; | 
 | 	SlSockAddrIn_t sl_addr_in; | 
 | 	SlSockAddrIn6_t sl_addr_in6; | 
 | 	SlSocklen_t sl_addrlen; | 
 |  | 
 | 	if (to != NULL) { | 
 | 		/* Translate to sl_SendTo() parameters: */ | 
 | 		sl_addr = translate_z_to_sl_addrs(to, tolen, &sl_addr_in, | 
 | 						  &sl_addr_in6, &sl_addrlen); | 
 |  | 
 | 		if (sl_addr == NULL) { | 
 | 			retval = SL_RET_CODE_INVALID_INPUT; | 
 | 			goto exit; | 
 | 		} | 
 |  | 
 | 		retval = sl_SendTo(sd, buf, (uint16_t)len, flags, | 
 | 				   sl_addr, sl_addrlen); | 
 | 	} else { | 
 | 		retval = (ssize_t)sl_Send(sd, buf, len, flags); | 
 | 	} | 
 |  | 
 | exit: | 
 | 	if (retval < 0) { | 
 | 		retval = slcb_SetErrno(getErrno(retval)); | 
 | 	} | 
 |  | 
 | 	return retval; | 
 | } | 
 |  | 
 | static ssize_t simplelink_sendmsg(void *obj, const struct msghdr *msg, | 
 | 				  int flags) | 
 | { | 
 | 	errno = -ENOTSUP; | 
 | 	return -1; | 
 | } | 
 |  | 
 | /* Adds address info entry to a list */ | 
 | static int set_addr_info(const struct SlNetUtil_addrInfo_t *sl_ai, | 
 | 			 struct zsock_addrinfo **res) | 
 | { | 
 | 	struct zsock_addrinfo *ai; | 
 | 	struct sockaddr *ai_addr; | 
 | 	int retval = 0; | 
 |  | 
 | 	ai = calloc(1, sizeof(struct zsock_addrinfo)); | 
 | 	if (!ai) { | 
 | 		retval = DNS_EAI_MEMORY; | 
 | 		goto exit; | 
 | 	} else { | 
 | 		/* Now, alloc the embedded sockaddr struct: */ | 
 | 		ai_addr = calloc(1, sizeof(struct sockaddr)); | 
 | 		if (!ai_addr) { | 
 | 			retval = DNS_EAI_MEMORY; | 
 | 			free(ai); | 
 | 			goto exit; | 
 | 		} | 
 | 	} | 
 |  | 
 | 	/* Now, fill in the fields of res (addrinfo struct): */ | 
 | 	ai->ai_family = (sl_ai->ai_family == SL_AF_INET6 ? AF_INET6 : AF_INET); | 
 | 	ai->ai_socktype = (sl_ai->ai_socktype == SLNETSOCK_SOCK_DGRAM ? | 
 | 		SOCK_DGRAM : SOCK_STREAM); | 
 | 	ai->ai_protocol = (sl_ai->ai_protocol == SLNETSOCK_PROTO_UDP ? | 
 | 		IPPROTO_UDP : IPPROTO_TCP); | 
 |  | 
 | 	/* Fill sockaddr struct fields based on family: */ | 
 | 	if (ai->ai_family == AF_INET) { | 
 | 		SlNetSock_AddrIn_t *sl_addr = | 
 | 			(SlNetSock_AddrIn_t *)sl_ai->ai_addr; | 
 |  | 
 | 		net_sin(ai_addr)->sin_family = ai->ai_family; | 
 | 		net_sin(ai_addr)->sin_addr.s_addr = sl_addr->sin_addr.s_addr; | 
 | 		net_sin(ai_addr)->sin_port = sl_addr->sin_port; | 
 | 		ai->ai_addrlen = sizeof(struct sockaddr_in); | 
 | 	} else { | 
 | 		SlNetSock_AddrIn6_t *sl_addr = | 
 | 			(SlNetSock_AddrIn6_t *)sl_ai->ai_addr; | 
 |  | 
 | 		net_sin6(ai_addr)->sin6_family = ai->ai_family; | 
 | 		net_sin6(ai_addr)->sin6_addr.s6_addr32[0] = | 
 | 			sl_addr->sin6_addr._S6_un._S6_u32[0]; | 
 | 		net_sin6(ai_addr)->sin6_addr.s6_addr32[1] = | 
 | 			sl_addr->sin6_addr._S6_un._S6_u32[1]; | 
 | 		net_sin6(ai_addr)->sin6_addr.s6_addr32[2] = | 
 | 			sl_addr->sin6_addr._S6_un._S6_u32[2]; | 
 | 		net_sin6(ai_addr)->sin6_addr.s6_addr32[3] = | 
 | 			sl_addr->sin6_addr._S6_un._S6_u32[3]; | 
 | 		net_sin6(ai_addr)->sin6_port = sl_addr->sin6_port; | 
 | 		ai->ai_addrlen = sizeof(struct sockaddr_in6); | 
 | 	} | 
 | 	ai->ai_addr = ai_addr; | 
 | 	ai->ai_next = *res; | 
 | 	*res = ai; | 
 |  | 
 | exit: | 
 | 	return retval; | 
 | } | 
 |  | 
 | static int simplelink_getaddrinfo(const char *node, const char *service, | 
 | 				  const struct zsock_addrinfo *hints, | 
 | 				  struct zsock_addrinfo **res) | 
 | { | 
 | 	int32_t retval; | 
 | 	struct SlNetUtil_addrInfo_t sl_hints; | 
 | 	struct SlNetUtil_addrInfo_t *sl_res, *sl_ai; | 
 |  | 
 | 	/* Initialize sl_hints to the defaults */ | 
 | 	memset(&sl_hints, 0, sizeof(sl_hints)); | 
 |  | 
 | 	/* Check args: */ | 
 | 	if (!res) { | 
 | 		retval = DNS_EAI_NONAME; | 
 | 		goto exit; | 
 | 	} | 
 |  | 
 | 	if (hints) { | 
 | 		/* | 
 | 		 * SlNetUtil only supports AI_NUMERICHOST and AI_PASSIVE, so | 
 | 		 * the rest are ignored. | 
 | 		 */ | 
 | 		sl_hints.ai_flags |= ((hints->ai_flags & AI_PASSIVE) ? | 
 | 			SLNETUTIL_AI_PASSIVE : 0); | 
 | 		sl_hints.ai_flags |= ((hints->ai_flags & AI_NUMERICHOST) ? | 
 | 			SLNETUTIL_AI_NUMERICHOST : 0); | 
 | 		if (hints->ai_family == AF_UNSPEC) { | 
 | 			sl_hints.ai_family = SLNETSOCK_AF_UNSPEC; | 
 | 		} else { | 
 | 			sl_hints.ai_family = (hints->ai_family == AF_INET6 ? | 
 | 				SLNETSOCK_AF_INET6 : SLNETSOCK_AF_INET); | 
 | 		} | 
 | 		if (hints->ai_socktype == 0) { | 
 | 			sl_hints.ai_socktype = 0; | 
 | 		} else { | 
 | 			sl_hints.ai_socktype = | 
 | 				(hints->ai_socktype == SOCK_DGRAM ? | 
 | 				SLNETSOCK_SOCK_DGRAM : SLNETSOCK_SOCK_STREAM); | 
 | 		} | 
 | 		if (hints->ai_protocol == 0) { | 
 | 			sl_hints.ai_protocol = 0; | 
 | 		} else { | 
 | 			sl_hints.ai_protocol = | 
 | 				(hints->ai_protocol == IPPROTO_UDP ? | 
 | 				SLNETSOCK_PROTO_UDP : SLNETSOCK_PROTO_TCP); | 
 | 		} | 
 |  | 
 | 	} | 
 |  | 
 | 	/* Now, try to resolve host name: */ | 
 | 	retval = SlNetUtil_getAddrInfo(SLNETIF_ID_1, node, | 
 | 		service, &sl_hints, &sl_res); | 
 |  | 
 | 	if (retval < 0) { | 
 | 		LOG_ERR("Could not resolve name: %s, retval: %d", | 
 | 			    node, retval); | 
 | 		retval = DNS_EAI_NONAME; | 
 | 		goto exit; | 
 | 	} | 
 |  | 
 | 	sl_ai = sl_res; | 
 | 	*res = NULL; | 
 | 	while (sl_ai != NULL) { | 
 | 		retval = set_addr_info(sl_ai, res); | 
 | 		if (retval < 0) { | 
 | 			LOG_ERR("Unable to set address info, retval: %d", | 
 | 				retval); | 
 | 			goto exit; | 
 | 		} | 
 | 		sl_ai = sl_ai->ai_next; | 
 | 	} | 
 | 	SlNetUtil_freeAddrInfo(sl_res); | 
 | exit: | 
 | 	return retval; | 
 | } | 
 |  | 
 | static void simplelink_freeaddrinfo(struct zsock_addrinfo *res) | 
 | { | 
 | 	__ASSERT_NO_MSG(res); | 
 |  | 
 | 	free(res->ai_addr); | 
 | 	free(res); | 
 | } | 
 |  | 
 | static int simplelink_fcntl(int sd, int cmd, va_list args) | 
 | { | 
 | 	int retval = 0; | 
 | 	SlSockNonblocking_t enableOption; | 
 | 	SlSocklen_t optlen = sizeof(SlSockNonblocking_t); | 
 |  | 
 | 	switch (cmd) { | 
 | 	case F_GETFL: | 
 | 		retval = sl_GetSockOpt(sd, SL_SOL_SOCKET, SL_SO_NONBLOCKING, | 
 | 			(_u8 *)&enableOption, &optlen); | 
 | 		if (retval == 0) { | 
 | 			if (enableOption.NonBlockingEnabled) { | 
 | 				retval |= O_NONBLOCK; | 
 | 			} | 
 | 		} | 
 | 		break; | 
 | 	case F_SETFL: | 
 | 		if ((va_arg(args, int) & O_NONBLOCK) != 0) { | 
 | 			enableOption.NonBlockingEnabled = 1; | 
 | 		} else { | 
 | 			enableOption.NonBlockingEnabled = 0; | 
 | 		} | 
 | 		retval = sl_SetSockOpt(sd, SL_SOL_SOCKET, SL_SO_NONBLOCKING, | 
 | 			&enableOption, optlen); | 
 | 		break; | 
 | 	default: | 
 | 		LOG_ERR("Invalid command: %d", cmd); | 
 | 		retval = slcb_SetErrno(EINVAL); | 
 | 		goto exit; | 
 | 	} | 
 |  | 
 | 	if (retval < 0) { | 
 | 		retval = slcb_SetErrno(getErrno(retval)); | 
 | 	} | 
 |  | 
 | exit: | 
 | 	return retval; | 
 | } | 
 |  | 
 | static int simplelink_ioctl(void *obj, unsigned int request, va_list args) | 
 | { | 
 | 	int sd = OBJ_TO_SD(obj); | 
 |  | 
 | 	switch (request) { | 
 | 	case ZFD_IOCTL_POLL_PREPARE: | 
 | 		return -EXDEV; | 
 |  | 
 | 	case ZFD_IOCTL_POLL_UPDATE: | 
 | 		return -EOPNOTSUPP; | 
 |  | 
 | 	case ZFD_IOCTL_POLL_OFFLOAD: { | 
 | 		struct zsock_pollfd *fds; | 
 | 		int nfds; | 
 | 		int timeout; | 
 |  | 
 | 		fds = va_arg(args, struct zsock_pollfd *); | 
 | 		nfds = va_arg(args, int); | 
 | 		timeout = va_arg(args, int); | 
 |  | 
 | 		return simplelink_poll(fds, nfds, timeout); | 
 | 	} | 
 |  | 
 | 	/* Otherwise, just forward to offloaded fcntl() | 
 | 	 * In Zephyr, fcntl() is just an alias of ioctl(). | 
 | 	 */ | 
 | 	default: | 
 | 		return simplelink_fcntl(sd, request, args); | 
 | 	} | 
 | } | 
 |  | 
 | static ssize_t simplelink_read(void *obj, void *buffer, size_t count) | 
 | { | 
 | 	return simplelink_recvfrom(obj, buffer, count, 0, NULL, 0); | 
 | } | 
 |  | 
 | static ssize_t simplelink_write(void *obj, const void *buffer, | 
 | 					  size_t count) | 
 | { | 
 | 	return simplelink_sendto(obj, buffer, count, 0, NULL, 0); | 
 | } | 
 |  | 
 | static const struct socket_op_vtable simplelink_socket_fd_op_vtable = { | 
 | 	.fd_vtable = { | 
 | 		.read = simplelink_read, | 
 | 		.write = simplelink_write, | 
 | 		.close = simplelink_close, | 
 | 		.ioctl = simplelink_ioctl, | 
 | 	}, | 
 | 	.bind = simplelink_bind, | 
 | 	.connect = simplelink_connect, | 
 | 	.listen = simplelink_listen, | 
 | 	.accept = simplelink_socket_accept, | 
 | 	.sendto = simplelink_sendto, | 
 | 	.sendmsg = simplelink_sendmsg, | 
 | 	.recvfrom = simplelink_recvfrom, | 
 | 	.getsockopt = simplelink_getsockopt, | 
 | 	.setsockopt = simplelink_setsockopt, | 
 | }; | 
 |  | 
 | static bool simplelink_is_supported(int family, int type, int proto) | 
 | { | 
 | 	/* TODO offloading always enabled for now. */ | 
 | 	return true; | 
 | } | 
 |  | 
 | static int simplelink_socket_create(int family, int type, int proto) | 
 | { | 
 | 	int fd = z_reserve_fd(); | 
 | 	int sock; | 
 |  | 
 | 	if (fd < 0) { | 
 | 		return -1; | 
 | 	} | 
 |  | 
 | 	sock = simplelink_socket(family, type, proto); | 
 | 	if (sock < 0) { | 
 | 		z_free_fd(fd); | 
 | 		return -1; | 
 | 	} | 
 |  | 
 | 	z_finalize_fd(fd, SD_TO_OBJ(sock), | 
 | 		      (const struct fd_op_vtable *) | 
 | 					&simplelink_socket_fd_op_vtable); | 
 |  | 
 | 	return fd; | 
 | } | 
 |  | 
 | static int simplelink_socket_accept(void *obj, struct sockaddr *addr, | 
 | 			     socklen_t *addrlen) | 
 | { | 
 | 	int fd = z_reserve_fd(); | 
 | 	int sock; | 
 |  | 
 | 	if (fd < 0) { | 
 | 		return -1; | 
 | 	} | 
 |  | 
 | 	sock = simplelink_accept(obj, addr, addrlen); | 
 | 	if (sock < 0) { | 
 | 		z_free_fd(fd); | 
 | 		return -1; | 
 | 	} | 
 |  | 
 | 	z_finalize_fd(fd, SD_TO_OBJ(sock), | 
 | 		      (const struct fd_op_vtable *) | 
 | 					&simplelink_socket_fd_op_vtable); | 
 |  | 
 | 	return fd; | 
 | } | 
 |  | 
 | #ifdef CONFIG_NET_SOCKETS_OFFLOAD | 
 | NET_SOCKET_REGISTER(simplelink, AF_UNSPEC, simplelink_is_supported, | 
 | 		    simplelink_socket_create); | 
 | #endif | 
 |  | 
 | void simplelink_sockets_init(void) | 
 | { | 
 | } | 
 |  | 
 | const struct socket_dns_offload simplelink_dns_ops = { | 
 | 	.getaddrinfo = simplelink_getaddrinfo, | 
 | 	.freeaddrinfo = simplelink_freeaddrinfo, | 
 | }; |