322 lines
9.8 KiB
C
322 lines
9.8 KiB
C
#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;
|
||
}
|
||
}
|