innopol-protocol-mayak-server/clang_client/Drivers/sim808.c

322 lines
9.8 KiB
C
Raw Permalink 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.

#define _POSIX_C_SOURCE 200809L
#include "sim808.h"
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/time.h>
/* -------------------- Внутреннее состояние (аналог глобалей из прошивки) -------------------- */
static int g_sock = -1;
static char* g_packet_buf = NULL;
static size_t g_packet_len = 0;
static size_t g_packet_cap = 0;
static const size_t PACKET_INIT_CAP = 4096; /* стартовая ёмкость буфера */
static const size_t PACKET_MAX_CAP = (size_t)32 * 1024 * 1024; /* ограничение от дурака: 32 МБ */
/* -------------------- Утилиты -------------------- */
static sim800_stat_t ensure_capacity(size_t need_extra)
{
if (g_packet_len + need_extra <= g_packet_cap)
return SIM808_OK;
size_t new_cap = g_packet_cap ? g_packet_cap : PACKET_INIT_CAP;
while (new_cap < g_packet_len + need_extra) {
if (new_cap > PACKET_MAX_CAP / 2) { /* защита от переполнения и чрезмерного роста */
new_cap = PACKET_MAX_CAP;
break;
}
new_cap *= 2;
}
if (new_cap < g_packet_len + need_extra)
return SIM808_ERR;
char* p = (char*)realloc(g_packet_buf, new_cap);
if (!p) return SIM808_ERR;
g_packet_buf = p;
g_packet_cap = new_cap;
return SIM808_OK;
}
static void packet_reset_buffer(void)
{
free(g_packet_buf);
g_packet_buf = NULL;
g_packet_len = 0;
g_packet_cap = 0;
}
/* Получить строковый IP из sockaddr (локальный/удалённый) */
static sim800_stat_t sockaddr_to_ipstr(const struct sockaddr* sa, char* out, size_t outsz)
{
if (!sa || !out || outsz == 0) return SIM808_ERR;
void* addr_ptr = NULL;
if (sa->sa_family == AF_INET) {
addr_ptr = &((struct sockaddr_in*)sa)->sin_addr;
} else if (sa->sa_family == AF_INET6) {
addr_ptr = &((struct sockaddr_in6*)sa)->sin6_addr;
} else {
return SIM808_ERR;
}
const char* r = inet_ntop(sa->sa_family, addr_ptr, out, (socklen_t)outsz);
return (r ? SIM808_OK : SIM808_ERR);
}
/* -------------------- Реализация API -------------------- */
sim800_stat_t sim808_init(void)
{
/* На ПК ничего инициализировать не нужно */
return SIM808_OK;
}
sim800_stat_t sim808_open_connection(char* apn, char* host, char* port, char* self_ip)
{
(void)apn; /* на ПК APN не используется */
/* Если сокет уже открыт — закроем */
if (g_sock >= 0) {
close(g_sock);
g_sock = -1;
}
struct addrinfo hints;
memset(&hints, 0, sizeof(hints));
hints.ai_socktype = SOCK_STREAM;
hints.ai_family = AF_UNSPEC; /* IPv4 или IPv6 */
hints.ai_protocol = IPPROTO_TCP;
struct addrinfo* res = NULL;
int gai = getaddrinfo(host, port, &hints, &res);
if (gai != 0 || !res) {
return SIM808_ERR;
}
int sockfd = -1;
struct addrinfo* it = res;
for (; it; it = it->ai_next) {
sockfd = (int)socket(it->ai_family, it->ai_socktype, it->ai_protocol);
if (sockfd < 0) continue;
/* Можно поставить таймауты (опционально) */
struct timeval tv;
tv.tv_sec = 10;
tv.tv_usec = 0;
setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
setsockopt(sockfd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv));
if (connect(sockfd, it->ai_addr, (socklen_t)it->ai_addrlen) == 0) {
g_sock = sockfd;
break;
}
close(sockfd);
sockfd = -1;
}
freeaddrinfo(res);
if (g_sock < 0) return SIM808_ERR;
/* По запросу — вернуть локальный IP интерфейса, через который установилось соединение */
if (self_ip) {
struct sockaddr_storage sa_local;
socklen_t slen = (socklen_t)sizeof(sa_local);
if (getsockname(g_sock, (struct sockaddr*)&sa_local, &slen) == 0) {
if (sockaddr_to_ipstr((struct sockaddr*)&sa_local, self_ip, 64) != SIM808_OK) {
/* если не смогли — просто оставим как есть */
self_ip[0] = '\0';
}
} else {
self_ip[0] = '\0';
}
}
return SIM808_OK;
}
sim800_stat_t sim808_close_connection(void)
{
if (g_sock >= 0) {
close(g_sock);
g_sock = -1;
}
return SIM808_OK;
}
sim800_stat_t packet_start(void)
{
packet_reset_buffer();
/* выделим стартовую ёмкость сразу, чтобы меньше реаллокаций */
g_packet_buf = (char*)malloc(PACKET_INIT_CAP);
if (!g_packet_buf) {
g_packet_cap = 0;
return SIM808_ERR;
}
g_packet_cap = PACKET_INIT_CAP;
g_packet_len = 0;
return SIM808_OK;
}
sim800_stat_t packet_data(char* msg)
{
if (!msg) return SIM808_ERR;
if (g_sock < 0) return SIM808_ERR; /* нет соединения */
if (!g_packet_buf && packet_start() != SIM808_OK) /* безопасности ради */
return SIM808_ERR;
size_t n = strlen(msg); /* как и в вашей версии — работаем со строками */
if (n == 0) return SIM808_OK;
if (ensure_capacity(n) != SIM808_OK)
return SIM808_ERR;
memcpy(g_packet_buf + g_packet_len, msg, n);
g_packet_len += n;
return SIM808_OK;
}
sim800_stat_t packet_end(void)
{
if (g_sock < 0) {
packet_reset_buffer();
return SIM808_ERR;
}
size_t total = g_packet_len;
size_t sent = 0;
while (sent < total) {
ssize_t r = send(g_sock, g_packet_buf + sent, total - sent, 0);
if (r < 0) {
if (errno == EINTR) continue;
packet_reset_buffer();
return SIM808_ERR;
}
if (r == 0) { /* неожиданное закрытие */
packet_reset_buffer();
return SIM808_ERR;
}
sent += (size_t)r;
}
packet_reset_buffer();
return SIM808_OK;
}
sim800_stat_t sim808_send_msg(char* msg)
{
sim800_stat_t ret = SIM808_OK;
ret = packet_start();
if (ret != SIM808_OK) return ret;
ret = packet_data(msg);
if (ret != SIM808_OK) { packet_reset_buffer(); return ret; }
ret = packet_end();
return ret;
}
/* -------------------- Заглушки “модемных” функций -------------------- */
sim800_stat_t reset(void) { return SIM808_OK; }
sim800_stat_t echo_off(void) { return SIM808_OK; }
sim800_stat_t check_sim_connection(void) { return SIM808_OK; }
sim800_stat_t chech_sim_card(void) { return SIM808_OK; }
sim800_stat_t check_network_registration(void) { return SIM808_OK; }
sim800_stat_t check_gprs_registration(void) { return SIM808_OK; }
sim800_stat_t close_ip_sessions(void) { return SIM808_OK; }
sim800_stat_t set_single_ip_mode(void) { return SIM808_OK; }
sim800_stat_t set_apn(char* apn, char* user, char* pass) { (void)apn; (void)user; (void)pass; return SIM808_OK; }
sim800_stat_t start_gprs(void) { return SIM808_OK; }
/* Возвращаем локальный IP уже открытого сокета */
sim800_stat_t get_ip(char* ip_ret)
{
if (!ip_ret) return SIM808_ERR;
if (g_sock < 0) { ip_ret[0] = '\0'; return SIM808_ERR; }
struct sockaddr_storage sa_local;
socklen_t slen = (socklen_t)sizeof(sa_local);
if (getsockname(g_sock, (struct sockaddr*)&sa_local, &slen) != 0) {
ip_ret[0] = '\0';
return SIM808_ERR;
}
return sockaddr_to_ipstr((struct sockaddr*)&sa_local, ip_ret, 64);
}
/* Непосредственные TCP-обёртки (оставлены для совместимости) */
sim800_stat_t open_tcp(char* host, char* port)
{
return sim808_open_connection(NULL, host, port, NULL);
}
sim800_stat_t close_tcp(void)
{
return sim808_close_connection();
}
/* -------------------- Заглушки обработчиков/таймеров -------------------- */
void sim808_gprs_usart_handler(void) { /* на ПК не используется */ }
void sim808_timeout_processing(void) { /* на ПК не используется */ }
/* -------------------- Демонстрационный стейт-машин (как в вашем исходнике) -------------------- */
static unsigned char self_ip_buf[64] = {0};
enum {
CMD_NONE = 0,
CMD_SIM808_OPEN_CONNECTION,
CMD_SIM808_SEND_SMG,
CMD_SIM808_CLOSE_CONNECTION,
};
static unsigned char cmd = 1;
static sim800_stat_t conn_open = SIM808_ERR;
static sim800_stat_t conn_clos = SIM808_ERR;
static sim800_stat_t msg_sent = SIM808_ERR;
void sim808_demo_send(void)
{
switch (cmd)
{
case CMD_SIM808_OPEN_CONNECTION:
cmd = CMD_NONE;
conn_open = SIM808_ERR;
conn_open = sim808_open_connection(
"unused-apn", /* игнорируется */
(char*)"158.160.43.29", /* или домен */
(char*)"9090",
(char*)self_ip_buf /* вернём локальный IP интерфейса */
);
break;
case CMD_SIM808_SEND_SMG:
cmd = CMD_NONE;
msg_sent = SIM808_ERR;
msg_sent = sim808_send_msg("biba");
break;
case CMD_SIM808_CLOSE_CONNECTION:
cmd = CMD_NONE;
conn_clos = SIM808_ERR;
conn_clos = sim808_close_connection();
break;
default:
break;
}
}