Files
2018-07-19 16:29:26 +08:00

334 lines
6.9 KiB
C

// Copyright 2018 Espressif Systems (Shanghai) PTE 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.
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <sys/errno.h>
#include "esp_socket.h"
#include "net/sockio.h"
#define CRITICAL_DECLARE(t)
#define CRITICAL_ENTER(t)
#define CRITICAL_EXIT(t)
#ifndef ESP_SOCKET_MAX
#define ESP_SOCKET_MAX 3
#endif
#define SET_ERR(err) errno = err
#define CHECK_FD(s) \
if (s >= ESP_SOCKET_MAX \
|| !s_socket[s].info) { \
SET_ERR(EINVAL); \
return -1; \
}
#define CHECK_METHOD(s, io) \
CHECK_FD(s) \
if (!s_socket[s].method \
|| !s_socket[s].method->io) { \
SET_ERR(ESRCH); \
return -1; \
}
#define SOCKET_IO_METHOD(s, io, ...) \
s_socket[s].method->io(s_socket[s].index, ##__VA_ARGS__)
/*
* socket event object
*/
typedef struct esp_socket_event {
esp_aio_cb_t cb;
void *arg;
} esp_socket_event_t;
/*
* socket object
*/
typedef struct esp_socket {
esp_socket_info_t *info;
/*
* lowlevel socket module index
*/
void *index;
/*
* lowlevel socket module method
*/
const esp_socket_method_t *method;
esp_socket_event_t event[ESP_SOCKET_MAX_EVENT];
} esp_socket_t;
static esp_socket_t s_socket[ESP_SOCKET_MAX];
static inline int event_is_used(int s, int e)
{
return s_socket[s].event[e].cb != NULL;
}
static inline int alloc_event(int s, int e)
{
CRITICAL_DECLARE(t);
if (e >= ESP_SOCKET_MAX_EVENT)
return -1;
CRITICAL_ENTER(t);
if (event_is_used(s, e)) {
e = ESP_SOCKET_MAX_EVENT;
}
CRITICAL_EXIT(t);
return e < ESP_SOCKET_MAX_EVENT ? e : -1;
}
static inline void free_event(int s, int e)
{
s_socket[s].event[e].cb = NULL;
}
static inline int alloc_socket(void)
{
int s;
CRITICAL_DECLARE(t);
CRITICAL_ENTER(t);
for (s = 0; s < ESP_SOCKET_MAX; s++) {
if (s_socket[s].info == NULL) {
s_socket[s].info = (void *)1;
break;
}
}
CRITICAL_EXIT(t);
return s < ESP_SOCKET_MAX ? s : -1;
}
static inline void free_socket(int s)
{
int e;
s_socket[s].info = NULL;
for (e = 0; e < ESP_SOCKET_MAX_EVENT; e++) {
free_event(s, e);
}
}
/*
* @brief create a socket file description
*/
int esp_socket(int domain, int type, int protocol)
{
int s;
s = alloc_socket();
if (s < 0) {
SET_ERR(ENOMEM);
return -1;
}
s_socket[s].info = malloc(sizeof(esp_socket_info_t));
if (!s_socket[s].info) {
free_socket(s);
SET_ERR(ENOMEM);
return -1;
}
s_socket[s].info->domain = domain;
s_socket[s].info->type = type;
s_socket[s].info->protocol = protocol;
s_socket[s].index = NULL;
s_socket[s].method = NULL;
return s;
}
/*
* @brief send a block of data asynchronously and receive result by callback function
*/
int esp_aio_sendto(esp_aio_t *aio, const struct sockaddr_ll *to, socklen_t len)
{
int s = aio->fd;
CHECK_METHOD(s, aio_sendto);
return SOCKET_IO_METHOD(s, aio_sendto, aio, to, len);
}
/*
* @brief register a event and its callback function to target of file description
*/
int esp_aio_event(int fd, unsigned int event, esp_aio_cb_t cb, void *arg)
{
int e;
int ret;
int s = fd;
CHECK_METHOD(s, aio_event);
e = alloc_event(s, event);
if (e < 0) {
SET_ERR(ENOMEM);
return -1;
}
ret = SOCKET_IO_METHOD(s, aio_event, event, cb, arg);
if (ret) {
free_event(s, e);
return -1;
}
s_socket[s].event[e].cb = cb;
s_socket[s].event[e].arg = arg;
return 0;
}
/*
* @brief lowlevel socket module upload event and its data
*/
int esp_upload_event(void *index, esp_socket_info_t *info, unsigned int event, esp_aio_data_t *aio_data)
{
int ret;
int s;
if (event >= ESP_SOCKET_MAX_EVENT)
return -EINVAL;
for (s = 0; s < ESP_SOCKET_MAX; s++) {
if (s_socket[s].index == index && event_is_used(s, event)) {
esp_aio_t aio;
aio.fd = s;
aio.cb = s_socket[s].event[event].cb;
aio.arg = s_socket[s].event[event].arg;
aio.pbuf = aio_data->pbuf;
aio.len = aio_data->len;
aio.ret = aio_data->status;
ret = s_socket[s].event[event].cb(&aio);
if (ret)
return ret;
}
}
return 0;
}
/*
* @brief free buffer taken from event callback
*/
int esp_free_pbuf(int fd, void *pbuf)
{
int ret;
int s = fd;
CHECK_METHOD(s, free_pbuf);
ret = SOCKET_IO_METHOD(s, free_pbuf, pbuf);
if (ret)
return -1;
return 0;
}
/*
* @brief map real lowlevel socket object to virtual socket
*/
static int map_socket_ll(int fd, const char *name)
{
int s = fd;
const esp_socket_method_t *p;
extern const esp_socket_method_t __start_ksymatabesp_socket, __stop_ksymatabesp_socket;
for (p = &__start_ksymatabesp_socket; p != &__stop_ksymatabesp_socket; p++) {
if (!strcmp(name, p->name))
break;
}
if (p >= &__stop_ksymatabesp_socket) {
SET_ERR(ENXIO);
return -1;
}
s_socket[s].index = p->open(s_socket[s].info);
if (!s_socket[s].index)
return -1;
s_socket[s].method = p;
return 0;
}
/*
* @brief send requset command to target by file description and get the result synchronously
*/
int esp_ioctl(int fd, unsigned int cmd, ...)
{
int ret;
va_list va;
int s = fd;
va_start(va, cmd);
switch(cmd) {
#if SIOCGIFINDEX != SIOGIFINDEX
case SIOGIFINDEX:
#endif
case SIOCGIFINDEX: {
const char *name;
name = va_arg(va, const char *);
ret = map_socket_ll(fd, name);
break;
}
default: {
int *arg = ((int *)&cmd) + 1;
CHECK_METHOD(s, ioctl);
ret = SOCKET_IO_METHOD(s, ioctl, cmd, arg);
break;
}
}
va_end(va);
return ret;
}
/*
* @brief close target of file description
*/
int esp_close(int fd)
{
int ret;
int s = fd;
CHECK_METHOD(s, close);
ret = SOCKET_IO_METHOD(s, close);
if (ret)
return -1;
free(s_socket[s].info);
free_socket(s);
return 0;
}