Files
E502_ADC_BF_PC_companion/lib/e502/e502api_tcp.c

903 lines
32 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#ifdef ENABLE_TCP
#include "e502api_private.h"
#include "e502_tcp_protocol.h"
#include "ltimer.h"
#include "e502_fpga_regs.h"
#include "e502api_tcp_private.h"
#include "osspec.h"
#include <stdlib.h>
#include <string.h>
#if defined _WIN32
#include <winsock2.h>
typedef int socklen_t;
typedef SOCKET t_socket;
#define SOCK_ERR_SIGBREAK() 0
#define L_SOCK_LAST_ERR_BLOCK() (WSAEWOULDBLOCK == WSAGetLastError())
#define L_SOCK_LAST_ERR_RESET() (WSAECONNRESET == WSAGetLastError())
#else
#include <errno.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <sys/select.h>
#include <netinet/tcp.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <unistd.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <sys/ioctl.h>
//#include <stropts.h>
#include <linux/sockios.h>
typedef int t_socket;
#define INVALID_SOCKET -1
#define SOCKET_ERROR -1
#define SOCK_ERR_SIGBREAK() (EINTR == errno)
#define L_SOCK_LAST_ERR_BLOCK() ((EAGAIN==errno) || (EWOULDBLOCK==errno))
#ifdef ECONNRESET
#define L_SOCK_LAST_ERR_RESET() (ECONNRESET==errno)
#endif
#define closesocket(sock) close(sock)
#endif
#ifndef MSG_NOSIGNAL
#define MSG_NOSIGNAL 0
#endif
#define E502_TCP_REQ_TOUT 5000
#define E502_TCP_STOP_WAIT_TOUT 5000
#define X502_MUTEX_TCP_IOCTL_LOCK_TOUT 5000
#define X502_MUTEX_TCP_DATA_LOCK_TOUT 5000
#define TCP_CTL_REQ_MAX_SIZE 512
#define TCP_IN_STREAM_BUF_MIN 128
#define TCP_IOCTL_INLINE_MAX_DATA_SIZE 64
#if 0
#define dprintf(...) fprintf(stderr, __VA_ARGS__)
#else
#define dprintf(...)
#endif
typedef struct {
t_socket cmd_sock;
t_socket data_sock;
uint32_t ip_addr;
uint32_t open_tout;
uint16_t data_port;
t_mutex ioctl_mutex;
t_mutex data_mutex;
uint32_t data_chs_en;
uint32_t recv_part_wrd; /**< принятое неполностью слово */
uint32_t send_part_wrd; /**< переданное неполностью слово */
uint8_t recv_part_size; /**< кол-во принятых байт в последнем неполном слове */
uint8_t send_part_size; /**< кол-во неотправленных байт в последнем переданном не полностью слове */
} t_tcp_iface_data;
static int32_t f_iface_free_devinfo_ptr(t_x502_devrec_inptr *devinfo_ptr);
static int32_t f_iface_open(t_x502_hnd hnd, const t_x502_devrec *devrec);
static int32_t f_iface_close(t_x502_hnd hnd);
static int32_t f_iface_stream_cfg(t_x502_hnd hnd, uint32_t ch, t_x502_stream_ch_params *params);
static int32_t f_iface_stream_start(t_x502_hnd hnd, uint32_t ch, uint32_t signle);
static int32_t f_iface_stream_stop(t_x502_hnd hnd, uint32_t ch, uint32_t flags);
static int32_t f_iface_stream_free(t_x502_hnd hnd, uint32_t ch, uint32_t flags);
static int32_t f_iface_stream_read(t_x502_hnd hnd, uint32_t *buf, uint32_t size, uint32_t tout);
static int32_t f_iface_stream_write(t_x502_hnd hnd, const uint32_t *buf, uint32_t size, uint32_t tout) ;
static int32_t f_iface_stream_get_rdy_cnt(t_x502_hnd hnd, uint32_t ch, uint32_t *rdy_cnt);
static int32_t f_iface_gen_ioctl(t_x502_hnd hnd, uint32_t cmd_code, uint32_t param,
const void* snd_data, uint32_t snd_size,
void* rcv_data, uint32_t recv_size, uint32_t* recvd_size,
uint32_t tout);
static const t_x502_dev_iface f_tcp_iface = {
E502_REGS_ARM_HARD_ID,
TCP_IN_STREAM_BUF_MIN,
TCP_CTL_REQ_MAX_SIZE,
TCP_CTL_REQ_MAX_SIZE/4,
TCP_CTL_REQ_MAX_SIZE, //flash rd size
TCP_CTL_REQ_MAX_SIZE, //flash wr size
f_iface_free_devinfo_ptr,
f_iface_open,
f_iface_close,
e502_iface_fpga_read,
e502_iface_fpga_write,
f_iface_stream_cfg,
f_iface_stream_start,
f_iface_stream_stop,
f_iface_stream_free,
e502_iface_stream_running,
f_iface_stream_read,
f_iface_stream_write,
f_iface_stream_get_rdy_cnt,
e502_iface_bf_mem_block_rd,
e502_iface_bf_mem_block_wr,
e502_iface_bf_firm_load,
e502_iface_flash_rd,
e502_iface_flash_wr,
e502_iface_flash_erase,
e502_iface_flash_set_prot,
e502_iface_reload_dev_info,
e502_iface_cycle_load_start,
e502_iface_cycle_setup,
e502_iface_cycle_stop,
e502_iface_cycle_check_setup,
e502_iface_fpga_mode_init,
f_iface_gen_ioctl,
e502_iface_check_feature
};
static void f_set_timeval_left(t_ltimer* tmr, struct timeval* tval) {
t_lclock_ticks left = ltimer_expiration(tmr);
tval->tv_sec = left / LCLOCK_TICKS_PER_SECOND;
tval->tv_usec = (left % LCLOCK_TICKS_PER_SECOND) * 1000000/LCLOCK_TICKS_PER_SECOND;
}
static int32_t f_con_sock(t_socket *psock, uint32_t ip_addr, uint16_t port, uint32_t tout) {
int32_t err = X502_ERR_OK;
struct sockaddr_in peer;
int connected = 0;
t_socket s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (s == INVALID_SOCKET)
err = X502_ERR_SOCKET_OPEN;
/* Переводим сокет в неблокирующий режим работы */
if (err == X502_ERR_OK) {
#ifdef _WIN32
ULONG nonblocking = 1;
if (SOCKET_ERROR == ioctlsocket(s, FIONBIO, &nonblocking))
err = X502_ERR_SOCKET_OPEN;
#else
int n = fcntl(s, F_GETFL, 0);
if (fcntl(s, F_SETFL, n|O_NONBLOCK)==-1) {
err = X502_ERR_SOCKET_OPEN;
}
#endif
}
if (err == X502_ERR_OK) {
/* заполняем структуру с адресом LTR-сервера */
memset(&peer, 0, sizeof(peer));
peer.sin_family = AF_INET;
peer.sin_port = htons(port);
peer.sin_addr.s_addr = htonl(ip_addr);
}
while (!connected && (err==X502_ERR_OK)) {
t_ltimer tmr;
ltimer_set(&tmr, LTIMER_MS_TO_CLOCK_TICKS(tout));
if (SOCKET_ERROR == connect(s, (struct sockaddr*)&peer, sizeof(peer))) {
int sockerr = 0;
fd_set fd_w, fd_e;
#ifdef _WIN32
if (WSAEWOULDBLOCK != WSAGetLastError()) {
#else
if (errno != EINPROGRESS) {
#endif
err = X502_ERR_SOCKET_OPEN;
} else {
struct timeval tval;
FD_ZERO(&fd_w);
FD_SET(s, &fd_w);
FD_ZERO(&fd_e);
FD_SET(s, &fd_e);
f_set_timeval_left(&tmr, &tval);
if (select((int)s+1, NULL, &fd_w, &fd_e, &tval) < 1)
err = X502_ERR_CONNECTION_TOUT;
}
if (err == X502_ERR_OK) {
/* судя по man - если произошла ошибка, то сокет становится writable!
так что в fd_w тоже нужно проверять ошибку */
socklen_t optlen = sizeof(sockerr);
if (SOCKET_ERROR == getsockopt(s, SOL_SOCKET, SO_ERROR,
(char*)&sockerr, &optlen)) {
err = X502_ERR_SOCKET_OPEN;
} else if (sockerr) {
#ifdef EHOSTUNREACH
if (sockerr == EHOSTUNREACH) {
err = X502_ERR_HOST_UNREACHABLE;
}
#endif
#ifdef ECONNRESET
if (sockerr == ECONNRESET) {
err = X502_ERR_CONNECTION_RESET;
}
#endif
if (err == X502_ERR_OK)
err = X502_ERR_TCP_CONNECTION_ERROR;
}
}
/* проверяем, что соединились успешно */
if ((err == X502_ERR_OK) && !sockerr && (FD_ISSET(s, &fd_w)))
connected = 1;
} else {
/* удалось соединится без ожидания */
connected = 1;
}
} /* while (!connected && !err) */
if (err != X502_ERR_OK) {
if (s!=INVALID_SOCKET) {
closesocket(s);
}
} else if (psock!=NULL) {
*psock = s;
}
return err;
}
static int32_t f_recv(t_socket s, uint8_t *buf, uint32_t size, t_ltimer *ptmr) {
int32_t err = X502_ERR_OK;
uint32_t offset = 0;
int timed_out = 0;
fd_set fd_r;
struct timeval tval;
if ((err == X502_ERR_OK) && (size!=0)) {
while (!err && !timed_out && (offset < size)) {
FD_ZERO(&fd_r);
FD_SET(s, &fd_r);
f_set_timeval_left(ptmr, &tval);
switch (select((int)s+1, &fd_r, NULL, NULL, &tval)) {
case SOCKET_ERROR:
/* Если пришел сигнал, то это не ошибка приема.
* Но скорее всего управление стоит вернуть сразу, хотя
* может сделать опцию... */
if (SOCK_ERR_SIGBREAK()) {
ltimer_set(ptmr, 0);
timed_out = 1;
} else {
err = X502_ERR_RECV;
}
break;
case 0: // таймаут
timed_out = 1;
break;
default: { /* дождались готовности на чтение */
int res = recv(s, buf + offset, size - offset, 0);
if (SOCKET_ERROR == res) {
if (!L_SOCK_LAST_ERR_BLOCK()) {
err = X502_ERR_RECV;
}
} else if (0 == res) {
/* соединение закрыто */
err = X502_ERR_CONNECTION_CLOSED_BY_DEV;
} else {
offset += res;
}
}
break;
}
} /* switch (select(ch->sock+1, &fd_r, NULL, NULL, &tval)) */
}
return err ? err : (int32_t)offset;
}
int32_t f_send(t_socket s, const uint8_t *buf, uint32_t size, t_ltimer *ptmr) {
int32_t err = X502_ERR_OK;
uint32_t offset = 0;
int timed_out = 0;
fd_set fd_w;
if ((err == X502_ERR_OK) && (size != 0)) {
while ((err == X502_ERR_OK) && !timed_out && (offset < size)) {
/* Сначала пробуем сделать запись без ожидания */
int res = send(s, buf + offset, size - offset, MSG_NOSIGNAL);
if (res == SOCKET_ERROR) {
struct timeval tval;
if (L_SOCK_LAST_ERR_BLOCK()) {
/* Надо ждать освобождения сокета */
FD_ZERO(&fd_w);
FD_SET(s, &fd_w);
f_set_timeval_left(ptmr, &tval);
switch (select((int)s+1, NULL, &fd_w, NULL, &tval)) {
case SOCKET_ERROR:
if (SOCK_ERR_SIGBREAK()) {
ltimer_set(ptmr, 0);
timed_out = 1;
} else {
err = X502_ERR_SEND;
}
break;
case 0: // таймаут
timed_out = 1;
break;
default:
if (ltimer_expired(ptmr))
timed_out = 1;
break;
}
} else {
err = X502_ERR_SEND;
}
} else { // no error
offset += res;
}
}
}
return (err) ? err : (int)offset;
}
static int32_t f_recv_exact(t_socket s, uint8_t *buf, uint32_t size, t_ltimer *ptmr) {
int32_t ret = f_recv(s, buf, size, ptmr);
return ret == (int32_t)size ? X502_ERR_OK : ret < 0 ? ret : X502_ERR_RECV_INSUFFICIENT_WORDS;
}
static int32_t f_send_exact(t_socket s, const uint8_t *buf, uint32_t size, t_ltimer *ptmr) {
int32_t ret = f_send(s, buf, size, ptmr);
return ret == (int32_t)size ? X502_ERR_OK : ret < 0 ? ret : X502_ERR_SEND_INSUFFICIENT_WORDS;
}
static int32_t f_iface_gen_ioctl(t_x502_hnd hnd, uint32_t cmd_code, uint32_t param,
const void* snd_data, uint32_t snd_size,
void* rcv_data, uint32_t recv_size, uint32_t* recvd_size, uint32_t tout) {
struct {
t_e502_tcp_cmd_hdr hdr;
uint8_t data[TCP_IOCTL_INLINE_MAX_DATA_SIZE];
} cmd;
t_e502_tcp_resp_hdr cmd_resp;
int32_t err = X502_ERR_OK;
t_ltimer tmr;
t_tcp_iface_data* iface_data = (t_tcp_iface_data*)hnd->iface_data;
t_socket s = iface_data->cmd_sock;
err = osspec_mutex_lock(iface_data->ioctl_mutex, X502_MUTEX_TCP_IOCTL_LOCK_TOUT);
if (err == X502_ERR_OK) {
ltimer_set(&tmr, LTIMER_MS_TO_CLOCK_TICKS(tout == 0 ? E502_TCP_REQ_TOUT : tout));
cmd.hdr.sign = E502_TCP_CMD_SIGNATURE;
cmd.hdr.cmd = cmd_code;
cmd.hdr.par = param;
cmd.hdr.data_len = snd_size;
cmd.hdr.resp_len = recv_size;
/* чтобы избежать двух передач по TCP, если данных, передаваемых с командой,
* меньше заданного порога, то объединяем их вместе с заголовком и посылаем
* за один вызов send */
if (snd_size <= TCP_IOCTL_INLINE_MAX_DATA_SIZE) {
if (snd_size > 0)
memcpy(cmd.data, snd_data, snd_size);
err = f_send_exact(s, (uint8_t*)&cmd, E502_TCP_CMD_HDR_SIZE + snd_size, &tmr);
} else {
err = f_send_exact(s, (uint8_t*)&cmd.hdr, E502_TCP_CMD_HDR_SIZE, &tmr);
if (err == X502_ERR_OK) {
err = f_send_exact(s, snd_data, snd_size, &tmr);
}
}
if (err == X502_ERR_OK) {
err = f_recv_exact(s, (uint8_t*)&cmd_resp, E502_TCP_CMD_RESP_SIZE, &tmr);
if (err == X502_ERR_RECV_INSUFFICIENT_WORDS) {
err = X502_ERR_NO_CMD_RESPONSE;
}
}
if ((err == X502_ERR_OK) && (cmd_resp.len > 0)) {
if (cmd_resp.len > recv_size) {
err = X502_ERR_IOCTL_INVALID_RESP_SIZE;
} else {
err = f_recv_exact(s, rcv_data, cmd_resp.len, &tmr);
}
}
if (err == X502_ERR_OK) {
if (recvd_size != NULL) {
*recvd_size = cmd_resp.len;
} else if (cmd_resp.len != recv_size) {
err = X502_ERR_IOCTL_INVALID_RESP_SIZE;
}
}
if ((err == X502_ERR_OK) && (cmd_resp.res!=0)) {
err = cmd_resp.res;
}
osspec_mutex_release(iface_data->ioctl_mutex);
}
return err;
}
static int32_t f_iface_free_devinfo_ptr(t_x502_devrec_inptr *devinfo_ptr) {
t_tcp_devinfo_data *devinfo_data = (t_tcp_devinfo_data*)devinfo_ptr->iface_data;
#ifdef ENABLE_DNSSD
if ((devinfo_data != NULL) && (devinfo_data->svc_rec != NULL))
E502_EthSvcRecordFree(devinfo_data->svc_rec);
#endif
free(devinfo_ptr->iface_data);
free(devinfo_ptr);
return X502_ERR_OK;
}
static int32_t f_iface_open(t_x502_hnd hnd, const t_x502_devrec *devrec) {
int32_t err = X502_ERR_OK;
t_tcp_devinfo_data *devinfo_data = (t_tcp_devinfo_data*)devrec->internal->iface_data;
t_socket s = INVALID_SOCKET;
#ifdef ENABLE_DNSSD
if (devinfo_data->svc_rec) {
err = e502_svc_fill_devinfo(devinfo_data);
}
#endif
if (err == X502_ERR_OK) {
err = f_con_sock(&s, devinfo_data->ip_addr, devinfo_data->cmd_port, devinfo_data->open_tout);
if (err == X502_ERR_OK) {
int flag = 1;
if (setsockopt(s, IPPROTO_TCP, TCP_NODELAY, (char*)&flag, sizeof(flag))==SOCKET_ERROR)
err = X502_ERR_SOCKET_OPEN;
}
}
if (err == X502_ERR_OK) {
t_tcp_iface_data *iface_data = malloc(sizeof(t_tcp_iface_data));
if (iface_data == NULL) {
err = X502_ERR_MEMORY_ALLOC;
} else {
t_lboot_devinfo lboot_info;
iface_data->cmd_sock = s;
iface_data->data_sock = INVALID_SOCKET;
iface_data->ip_addr = devinfo_data->ip_addr;
iface_data->open_tout = devinfo_data->open_tout;
iface_data->data_port = devinfo_data->data_port;
iface_data->ioctl_mutex = osspec_mutex_create();
iface_data->data_mutex = osspec_mutex_create();
iface_data->data_chs_en = 0;
if ((iface_data->ioctl_mutex == OSSPEC_INVALID_MUTEX)
|| (iface_data->data_mutex == OSSPEC_INVALID_MUTEX)) {
err = X502_ERR_MUTEX_CREATE;
} else {
hnd->iface_data = iface_data;
err = hnd->iface_hnd->gen_ioctl(hnd, E502_CM4_CMD_GET_MODULE_INFO, 0, NULL, 0, &lboot_info,
sizeof(lboot_info), NULL, 0);
if (err == X502_ERR_OK) {
if (strcmp(lboot_info.devname, devrec->devname)) {
err = X502_ERR_INVALID_DEVICE;
} else {
e502_devinfo_init(&hnd->info, &lboot_info);
err = e502_fill_devflags(hnd);
}
}
}
if (err != X502_ERR_OK) {
if (iface_data->ioctl_mutex != OSSPEC_INVALID_MUTEX)
osspec_mutex_destroy(iface_data->ioctl_mutex);
if (iface_data->data_mutex != OSSPEC_INVALID_MUTEX)
osspec_mutex_destroy(iface_data->data_mutex);
hnd->iface_data = NULL;
free(iface_data);
}
}
}
if ((err != X502_ERR_OK) && (s != INVALID_SOCKET)) {
closesocket(s);
}
return err;
}
static int32_t f_iface_close(t_x502_hnd hnd) {
int32_t err = X502_ERR_OK;
t_tcp_iface_data *tcp_data = (t_tcp_iface_data*)hnd->iface_data;
if (tcp_data != NULL) {
if (tcp_data->data_sock!=INVALID_SOCKET) {
closesocket(tcp_data->data_sock);
tcp_data->data_sock = INVALID_SOCKET;
}
if (tcp_data->cmd_sock!=INVALID_SOCKET) {
closesocket(tcp_data->cmd_sock);
tcp_data->cmd_sock = INVALID_SOCKET;
}
if (tcp_data->ioctl_mutex != OSSPEC_INVALID_MUTEX) {
osspec_mutex_destroy(tcp_data->ioctl_mutex);
tcp_data->ioctl_mutex = OSSPEC_INVALID_MUTEX;
}
if (tcp_data->data_mutex != OSSPEC_INVALID_MUTEX) {
osspec_mutex_destroy(tcp_data->data_mutex);
tcp_data->data_mutex = OSSPEC_INVALID_MUTEX;
}
free(hnd->iface_data);
hnd->iface_data = NULL;
}
return err;
}
static int32_t f_iface_stream_cfg(t_x502_hnd hnd, uint32_t ch, t_x502_stream_ch_params *params) {
int32_t err = X502_ERR_OK;
t_tcp_iface_data *tcp_data = (t_tcp_iface_data*)hnd->iface_data;
err = osspec_mutex_lock(tcp_data->data_mutex, X502_MUTEX_TCP_DATA_LOCK_TOUT);
if (err == X502_ERR_OK) {
if (tcp_data->data_sock == INVALID_SOCKET) {
err = f_iface_gen_ioctl(hnd, E502_CM4_CMD_DROP_DATA_CON, 0, NULL, 0, NULL, 0, NULL, 0);
if (err == X502_ERR_OK) {
err = f_con_sock(&tcp_data->data_sock, tcp_data->ip_addr, tcp_data->data_port, tcp_data->open_tout);
}
if (err == X502_ERR_OK) {
int flag = 1;
if (setsockopt(tcp_data->data_sock, IPPROTO_TCP, TCP_NODELAY, (char*)&flag, sizeof(flag))==SOCKET_ERROR)
err = X502_ERR_SOCKET_OPEN;
}
}
if (err == X502_ERR_OK) {
unsigned buf_size = params->buf_size*4;
if (ch==X502_STREAM_CH_IN) {
tcp_data->recv_part_size = 0;
if (setsockopt(tcp_data->data_sock, SOL_SOCKET, SO_RCVBUF, (char*)&buf_size, sizeof(buf_size))==SOCKET_ERROR) {
err = X502_ERR_SOCKET_SET_BUF_SIZE;
}
else {
dprintf("set SO_RCVBUF to %d\n", buf_size);
}
#ifndef _WIN32
setsockopt(tcp_data->data_sock, SOL_SOCKET, SO_RCVBUFFORCE, (char*)&buf_size, sizeof(buf_size));
#endif
socklen_t opt_len = sizeof(buf_size);
if (getsockopt(tcp_data->data_sock, SOL_SOCKET, SO_RCVBUF, (char*)&buf_size, &opt_len) != SOCKET_ERROR) {
dprintf("get SO_RCVBUF = %d\n", buf_size);
}
#ifdef _WIN32
else {
dprintf("getsockopt error = %d\n", WSAGetLastError());
}
#endif
} else {
tcp_data->send_part_size = 0;
if (setsockopt(tcp_data->data_sock, SOL_SOCKET, SO_SNDBUF, (char*)&buf_size, sizeof(buf_size))==SOCKET_ERROR) {
err = X502_ERR_SOCKET_SET_BUF_SIZE;
}
}
}
if (err == X502_ERR_OK) {
tcp_data->data_chs_en |= (1UL << ch);
}
osspec_mutex_release(tcp_data->data_mutex);
}
return err;
}
static int32_t f_iface_stream_start(t_x502_hnd hnd, uint32_t ch, uint32_t flags) {
int32_t err = 0;
if (!err && !(flags & X502_STREAM_FLAG_NO_REQUEST)) {
err = f_iface_gen_ioctl(hnd, E502_CM4_CMD_STREAM_START, (ch<<16),
NULL, 0, NULL, 0, NULL, 0);
}
return err;
}
static int32_t f_iface_stream_stop(t_x502_hnd hnd, uint32_t ch, uint32_t flags) {
int32_t err = 0;
if (!(flags & X502_STREAM_FLAG_NO_REQUEST)) {
int32_t running;
err = hnd->iface_hnd->stream_running(hnd, ch, &running);
if (!err && running) {
err = f_iface_gen_ioctl(hnd, E502_CM4_CMD_STREAM_STOP, (ch << 16),
NULL, 0, NULL, 0, NULL, 0);
}
}
return err;
}
static int32_t f_iface_stream_free(t_x502_hnd hnd, uint32_t ch, uint32_t flags) {
t_tcp_iface_data *tcp_data = (t_tcp_iface_data *)hnd->iface_data;
int32_t err = osspec_mutex_lock(tcp_data->data_mutex, X502_MUTEX_TCP_DATA_LOCK_TOUT);
if (err == X502_ERR_OK) {
err = hnd->iface_hnd->stream_stop(hnd, ch, flags);
if (err == X502_ERR_OK) {
tcp_data->data_chs_en &= ~(1UL << ch);
if ((tcp_data->data_chs_en == 0) && (tcp_data->data_sock != INVALID_SOCKET)) {
closesocket(tcp_data->data_sock);
tcp_data->data_sock = INVALID_SOCKET;
}
}
osspec_mutex_release(tcp_data->data_mutex);
}
return err;
}
static int32_t f_iface_stream_read(t_x502_hnd hnd, uint32_t *buf, uint32_t size, uint32_t tout) {
t_tcp_iface_data *tcp_data = (t_tcp_iface_data*)hnd->iface_data;
int32_t recvd;
t_ltimer tmr;
if (tcp_data->data_sock == INVALID_SOCKET) {
recvd = X502_ERR_NO_DATA_CONNECTION;
} else {
ltimer_set(&tmr, LTIMER_MS_TO_CLOCK_TICKS(tout));
if (tcp_data->recv_part_size != 0) {
buf[0] = tcp_data->recv_part_wrd;
}
recvd = f_recv(tcp_data->data_sock, (uint8_t*)buf + tcp_data->recv_part_size,
size *sizeof(buf[0]) - tcp_data->recv_part_size, &tmr);
if (recvd > 0) {
recvd += tcp_data->recv_part_size;
tcp_data->recv_part_size = recvd % sizeof(buf[0]);
recvd /= sizeof(buf[0]);
if (tcp_data->recv_part_size!=0) {
tcp_data->recv_part_wrd = buf[recvd];
}
}
}
return recvd;
}
static int32_t f_iface_stream_write(t_x502_hnd hnd, const uint32_t *buf, uint32_t size, uint32_t tout) {
int32_t sent = 0;
t_ltimer tmr;
t_tcp_iface_data *tcp_data = (t_tcp_iface_data*)hnd->iface_data;
if (tcp_data->data_sock == INVALID_SOCKET) {
sent = X502_ERR_NO_DATA_CONNECTION;
} else {
ltimer_set(&tmr, LTIMER_MS_TO_CLOCK_TICKS(tout));
/* проверяем, не осталось ли не переданного некратного слова => если осталось
* то пробуем сперва дослать его */
if (tcp_data->send_part_size!=0) {
sent = f_send(tcp_data->data_sock, (uint8_t*)&tcp_data->send_part_wrd,
tcp_data->send_part_size, &tmr);
if (sent >= 0) {
tcp_data->send_part_size -= (uint8_t)sent;
if (tcp_data->send_part_size != 0) {
tcp_data->send_part_wrd >>= 8*sent;
}
sent = 0;
}
}
/* новые данные пересылаем только если старое неполное слово точно ушло */
if ((sent == 0) && (tcp_data->send_part_size==0)) {
sent = f_send(tcp_data->data_sock, (uint8_t*)buf, size * sizeof(buf[0]), &tmr);
if (sent >= 0) {
/* если не полностью передали последнее слово, то нужно сохранить
* остаток слова, чтобы потом передать его */
tcp_data->send_part_size = sent % sizeof(buf[0]);
sent /= sizeof(buf[0]);
if (tcp_data->send_part_size!=0) {
tcp_data->send_part_wrd = buf[sent] >> (8*tcp_data->send_part_size);
tcp_data->send_part_size = sizeof(buf[0]) - tcp_data->send_part_size;
sent++;
}
}
}
}
return sent;
}
static int32_t f_iface_stream_get_rdy_cnt(t_x502_hnd hnd, uint32_t ch, uint32_t *rdy_cnt) {
int32_t err = 0;
t_tcp_iface_data *tcp_data = (t_tcp_iface_data *)hnd->iface_data;
#ifdef _WIN32
if (ch == X502_STREAM_CH_IN) {
u_long val;
if (ioctlsocket(tcp_data->data_sock, FIONREAD, &val) == SOCKET_ERROR) {
err = X502_ERR_IOCTL_FAILD;
} else {
*rdy_cnt = val/4;
}
} else {
err = X502_ERR_NOT_IMPLEMENTED;
}
#else
if (ch == X502_STREAM_CH_IN) {
int val;
if (ioctl(tcp_data->data_sock, SIOCINQ, &val)==-1) {
err = X502_ERR_IOCTL_FAILD;
} else {
*rdy_cnt = val/4;
}
} else {
err = X502_ERR_NOT_IMPLEMENTED;
#if 0
/* Данный вариант в реальности не работает корректно */
int buf_len, val;
socklen_t optlen = sizeof(buf_len);
if (getsockopt(tcp_data->data_sock, SOL_SOCKET, SO_SNDBUF, (char*)&buf_len, &optlen)==SOCKET_ERROR) {
err = X502_ERR_IOCTL_FAILD;
} else if (ioctl(tcp_data->data_sock, SIOCOUTQ, &val)==-1) {
err = X502_ERR_IOCTL_FAILD;
} else {
*rdy_cnt = (buf_len - val)/4;
}
#endif
}
#endif
return err;
}
int32_t e502_make_tcp_rec(t_x502_devrec *devrec, uint32_t flags, uint32_t tout, char const *devname) {
int32_t err = (devrec == NULL) ? X502_ERR_INVALID_DEVICE_RECORD : X502_ERR_OK;
X502_DevRecordInit(devrec);
if (err==X502_ERR_OK) {
t_tcp_devinfo_data *devinfo_data = malloc(sizeof(t_tcp_devinfo_data));
t_x502_devrec_inptr *devinfo_ptr = malloc(sizeof(t_x502_devrec_inptr));
if ((devinfo_data==NULL) || (devinfo_ptr == NULL)) {
err = X502_ERR_MEMORY_ALLOC;
} else {
strcpy(devrec->devname, devname);
devinfo_data->cmd_port = E502_TCP_DEFAULT_CMD_PORT;
devinfo_data->data_port = E502_TCP_DEFAULT_DATA_PORT;
devinfo_data->open_tout = tout;
devinfo_data->flags = flags;
devinfo_ptr->iface = &f_tcp_iface;
devinfo_ptr->iface_data = devinfo_data;
devrec->internal = devinfo_ptr;
devrec->iface = X502_IFACE_ETH;
devrec->flags = X502_DEVFLAGS_IFACE_SUPPORT_USB | X502_DEVFLAGS_IFACE_SUPPORT_ETH;
}
if (err != X502_ERR_OK) {
free(devinfo_data);
free(devinfo_ptr);
}
}
return err;
}
X502_EXPORT(int32_t) E502_MakeDevRecordByIpAddr2(t_x502_devrec *devrec, uint32_t ip_addr,
uint32_t flags, uint32_t tout, char const *devname) {
int32_t err = e502_make_tcp_rec(devrec, flags, tout, devname);
if (err == X502_ERR_OK) {
t_tcp_devinfo_data *devinfo_data = (t_tcp_devinfo_data *)devrec->internal->iface_data;
devinfo_data->ip_addr = ip_addr;
#ifdef ENABLE_DNSSD
devinfo_data->svc_rec = NULL;
#endif
sprintf(devrec->location, "%d.%d.%d.%d",
(ip_addr>>24) & 0xFF,
(ip_addr>>16) & 0xFF,
(ip_addr>>8) & 0xFF,
(ip_addr>>0) & 0xFF);
devrec->location_type = X502_LOCATION_TYPE_ADDR;
}
return err;
}
X502_EXPORT(int32_t) E502_MakeDevRecordByIpAddr(t_x502_devrec *devrec, uint32_t ip_addr,
uint32_t flags, uint32_t tout) {
return E502_MakeDevRecordByIpAddr2(devrec, ip_addr, flags, tout, E502_DEVICE_NAME);
}
X502_EXPORT(int32_t) E502_EthDevRecordSetCmdPort(t_x502_devrec *devrec, uint16_t cmd_port) {
int32_t err = ((devrec == NULL) || (devrec->internal->iface != &f_tcp_iface)) ?
X502_ERR_INVALID_DEVICE_RECORD : X502_ERR_OK;
if (err == X502_ERR_OK) {
t_tcp_devinfo_data *devinfo_data = (t_tcp_devinfo_data *)devrec->internal->iface_data;
devinfo_data->cmd_port = cmd_port;
}
return err;
}
X502_EXPORT(int32_t) E502_EthDevRecordSetDataPort(t_x502_devrec *devrec, uint16_t data_port) {
int32_t err = ((devrec == NULL) || (devrec->internal->iface != &f_tcp_iface)) ?
X502_ERR_INVALID_DEVICE_RECORD : X502_ERR_OK;
if (err == X502_ERR_OK) {
t_tcp_devinfo_data *devinfo_data = (t_tcp_devinfo_data *)devrec->internal->iface_data;
devinfo_data->data_port = data_port;
}
return err;
}
X502_EXPORT(int32_t) E16_OpenByIpAddr(t_x502_hnd hnd, uint32_t ip_addr, uint32_t flags, uint32_t tout) {
int32_t err = X502_CHECK_HND(hnd);
if (err == X502_ERR_OK) {
t_x502_devrec devinfo;
err = E502_MakeDevRecordByIpAddr2(&devinfo, ip_addr, flags, tout, E16_DEVICE_NAME);
if (err == X502_ERR_OK) {
err = X502_OpenByDevRecord(hnd, &devinfo);
X502_FreeDevRecordList(&devinfo, 1);
}
}
return err;
}
X502_EXPORT(int32_t) E502_OpenByIpAddr(t_x502_hnd hnd, uint32_t ip_addr, uint32_t flags, uint32_t tout) {
int32_t err = X502_CHECK_HND(hnd);
if (err == X502_ERR_OK) {
t_x502_devrec devinfo;
err = E502_MakeDevRecordByIpAddr(&devinfo, ip_addr, flags, tout);
if (err == X502_ERR_OK) {
err = X502_OpenByDevRecord(hnd, &devinfo);
X502_FreeDevRecordList(&devinfo, 1);
}
}
return err;
}
X502_EXPORT(int32_t) E502_GetIpAddr(t_x502_hnd hnd, uint32_t *ip_addr) {
int32_t err = X502_CHECK_HND_OPENED(hnd);
if (err == X502_ERR_OK) {
if (hnd->iface != X502_IFACE_ETH) {
err = X502_ERR_INVALID_OP_FOR_IFACE;
} else {
t_tcp_iface_data *tcp_data = (t_tcp_iface_data *)hnd->iface_data;
*ip_addr = tcp_data->ip_addr;
}
}
return err;
}
#else
#include "e502api.h"
X502_EXPORT(int32_t) E502_MakeDevRecordByIpAddr(t_x502_devrec *devrec, uint32_t ip_addr,
uint32_t flags, uint32_t tout) {
return X502_ERR_NOT_IMPLEMENTED;
}
X502_EXPORT(int32_t) E502_EthDevRecordSetCmdPort(t_x502_devrec *devrec, uint16_t cmd_port) {
return X502_ERR_NOT_IMPLEMENTED;
}
X502_EXPORT(int32_t) E502_EthDevRecordSetDataPort(t_x502_devrec *devrec, uint16_t data_port) {
return X502_ERR_NOT_IMPLEMENTED;
}
X502_EXPORT(int32_t) E502_OpenByIpAddr(t_x502_hnd hnd, uint32_t ip_addr, uint32_t flags, uint32_t tout) {
return X502_ERR_NOT_IMPLEMENTED;
}
X502_EXPORT(int32_t) E502_GetIpAddr(t_x502_hnd hnd, uint32_t *ip_addr) {
return X502_ERR_NOT_IMPLEMENTED;
}
#endif