Implement APRS-IS interface type
Implement an APRS-IS interface type which is capable of receiving all APRS-IS traffic which can be represented in AX.25 frames; currently, there are no provisions for handling extended features such as non-numeric SSIDs, nor a client nor server which facilitate direct APRS-IS communication. Presently, APRS-IS traffic cannot be sent from this interface. Changes: * Add patty_ax25_aprs_is driver type in src/aprs_is.c * Implement 'aprs-is' interface configuration type in bin/if.c; make separate, smaller state machines for handling base interface config, KISS, and APRS-IS interface config
This commit is contained in:
parent
29fbea2e18
commit
a807a9ec9d
5 changed files with 544 additions and 18 deletions
140
bin/if.c
140
bin/if.c
|
@ -19,12 +19,29 @@ enum mode {
|
|||
MODE_IFOPTS,
|
||||
MODE_IFADDR,
|
||||
MODE_KISS,
|
||||
MODE_BAUD,
|
||||
MODE_FLOW
|
||||
MODE_APRS_IS,
|
||||
};
|
||||
|
||||
enum mode_kiss {
|
||||
MODE_KISS_DEVICE,
|
||||
MODE_KISS_IFOPTS,
|
||||
MODE_KISS_BAUD,
|
||||
MODE_KISS_FLOW,
|
||||
};
|
||||
|
||||
enum mode_aprs_is {
|
||||
MODE_APRS_IS_IFOPTS,
|
||||
MODE_APRS_IS_HOST,
|
||||
MODE_APRS_IS_PORT,
|
||||
MODE_APRS_IS_USER,
|
||||
MODE_APRS_IS_PASS,
|
||||
MODE_APRS_IS_APPNAME,
|
||||
MODE_APRS_IS_VERSION,
|
||||
MODE_APRS_IS_FILTER,
|
||||
};
|
||||
|
||||
static patty_ax25_if *create_kiss(struct context *ctx, int argc, char **argv) {
|
||||
enum mode mode = MODE_KISS;
|
||||
enum mode_kiss mode = MODE_KISS_DEVICE;
|
||||
|
||||
patty_kiss_tnc_info info;
|
||||
|
||||
|
@ -40,19 +57,19 @@ static patty_ax25_if *create_kiss(struct context *ctx, int argc, char **argv) {
|
|||
|
||||
for (i=0; i<argc; i++) {
|
||||
switch (mode) {
|
||||
case MODE_KISS:
|
||||
case MODE_KISS_DEVICE:
|
||||
info.flags |= PATTY_KISS_TNC_DEVICE;
|
||||
info.device = argv[i];
|
||||
|
||||
mode = MODE_IFOPTS;
|
||||
mode = MODE_KISS_IFOPTS;
|
||||
|
||||
break;
|
||||
|
||||
case MODE_IFOPTS:
|
||||
case MODE_KISS_IFOPTS:
|
||||
if (strcmp(argv[i], "baud") == 0) {
|
||||
mode = MODE_BAUD;
|
||||
mode = MODE_KISS_BAUD;
|
||||
} else if (strcmp(argv[i], "flow") == 0) {
|
||||
mode = MODE_FLOW;
|
||||
mode = MODE_KISS_FLOW;
|
||||
} else {
|
||||
patty_error_fmt(ctx->err, "Invalid parameter '%s'",
|
||||
argv[i]);
|
||||
|
@ -60,7 +77,7 @@ static patty_ax25_if *create_kiss(struct context *ctx, int argc, char **argv) {
|
|||
|
||||
break;
|
||||
|
||||
case MODE_BAUD:
|
||||
case MODE_KISS_BAUD:
|
||||
if (!(argv[i][0] >= '0' && argv[i][0] <= '9')) {
|
||||
patty_error_fmt(ctx->err, "Invalid baud rate '%s'",
|
||||
argv[i]);
|
||||
|
@ -71,11 +88,11 @@ static patty_ax25_if *create_kiss(struct context *ctx, int argc, char **argv) {
|
|||
info.flags |= PATTY_KISS_TNC_BAUD;
|
||||
info.baud = atoi(argv[i]);
|
||||
|
||||
mode = MODE_IFOPTS;
|
||||
mode = MODE_KISS_IFOPTS;
|
||||
|
||||
break;
|
||||
|
||||
case MODE_FLOW:
|
||||
case MODE_KISS_FLOW:
|
||||
if (strcmp(argv[i], "crtscts") == 0) {
|
||||
info.flags |= PATTY_KISS_TNC_FLOW;
|
||||
info.flow = PATTY_KISS_TNC_FLOW_CRTSCTS;
|
||||
|
@ -89,7 +106,7 @@ static patty_ax25_if *create_kiss(struct context *ctx, int argc, char **argv) {
|
|||
goto error_invalid;
|
||||
}
|
||||
|
||||
mode = MODE_IFOPTS;
|
||||
mode = MODE_KISS_IFOPTS;
|
||||
|
||||
break;
|
||||
|
||||
|
@ -107,6 +124,104 @@ error_invalid:
|
|||
return NULL;
|
||||
}
|
||||
|
||||
static patty_ax25_if *create_aprs_is(struct context *ctx,
|
||||
int argc,
|
||||
char **argv) {
|
||||
enum mode_aprs_is mode = MODE_APRS_IS_IFOPTS;
|
||||
|
||||
patty_ax25_aprs_is_info info = {
|
||||
.user = "N0CALL",
|
||||
.pass = "-1",
|
||||
.appname = PATTY_AX25_APRS_IS_DEFAULT_APPNAME,
|
||||
.version = PATTY_AX25_APRS_IS_DEFAULT_VERSION,
|
||||
.filter = "m/25"
|
||||
};
|
||||
|
||||
int i;
|
||||
|
||||
if (argc == 0) {
|
||||
patty_error_fmt(ctx->err, "Too few parameters for 'kiss' interface");
|
||||
|
||||
goto error_invalid;
|
||||
}
|
||||
|
||||
for (i=0; i<argc; i++) {
|
||||
switch (mode) {
|
||||
case MODE_APRS_IS_IFOPTS:
|
||||
if (strcmp(argv[i], "host") == 0) {
|
||||
mode = MODE_APRS_IS_HOST;
|
||||
} else if (strcmp(argv[i], "port") == 0) {
|
||||
mode = MODE_APRS_IS_PORT;
|
||||
} else if (strcmp(argv[i], "user") == 0) {
|
||||
mode = MODE_APRS_IS_USER;
|
||||
} else if (strcmp(argv[i], "pass") == 0) {
|
||||
mode = MODE_APRS_IS_PASS;
|
||||
} else if (strcmp(argv[i], "appname") == 0) {
|
||||
mode = MODE_APRS_IS_APPNAME;
|
||||
} else if (strcmp(argv[i], "version") == 0) {
|
||||
mode = MODE_APRS_IS_VERSION;
|
||||
} else if (strcmp(argv[i], "filter") == 0) {
|
||||
mode = MODE_APRS_IS_FILTER;
|
||||
} else {
|
||||
patty_error_fmt(ctx->err, "Invalid parameter '%s'",
|
||||
argv[i]);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case MODE_APRS_IS_HOST:
|
||||
info.host = argv[i];
|
||||
mode = MODE_APRS_IS_IFOPTS;
|
||||
|
||||
break;
|
||||
|
||||
case MODE_APRS_IS_PORT:
|
||||
info.port = argv[i];
|
||||
mode = MODE_APRS_IS_IFOPTS;
|
||||
|
||||
break;
|
||||
|
||||
case MODE_APRS_IS_USER:
|
||||
info.user = argv[i];
|
||||
mode = MODE_APRS_IS_IFOPTS;
|
||||
|
||||
break;
|
||||
|
||||
case MODE_APRS_IS_PASS:
|
||||
info.pass = argv[i];
|
||||
mode = MODE_APRS_IS_IFOPTS;
|
||||
|
||||
break;
|
||||
|
||||
case MODE_APRS_IS_APPNAME:
|
||||
info.appname = argv[i];
|
||||
mode = MODE_APRS_IS_IFOPTS;
|
||||
|
||||
break;
|
||||
|
||||
case MODE_APRS_IS_VERSION:
|
||||
info.version = argv[i];
|
||||
mode = MODE_APRS_IS_IFOPTS;
|
||||
|
||||
break;
|
||||
|
||||
case MODE_APRS_IS_FILTER:
|
||||
info.filter = argv[i];
|
||||
mode = MODE_APRS_IS_IFOPTS;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return patty_ax25_if_new( ctx->name,
|
||||
&ctx->addr,
|
||||
patty_ax25_aprs_is_driver(),
|
||||
&info);
|
||||
|
||||
error_invalid:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct if_type {
|
||||
const char *name;
|
||||
patty_ax25_if *(*func)(struct context *ctx, int argc, char **argv);
|
||||
|
@ -114,6 +229,7 @@ struct if_type {
|
|||
|
||||
struct if_type if_types[] = {
|
||||
{ "kiss", create_kiss },
|
||||
{ "aprs-is", create_aprs_is },
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
||||
|
|
|
@ -104,6 +104,7 @@ typedef struct _patty_ax25_if patty_ax25_if;
|
|||
#include <patty/ax25/frame.h>
|
||||
#include <patty/ax25/if.h>
|
||||
#include <patty/kiss/tnc.h>
|
||||
#include <patty/ax25/aprs_is.h>
|
||||
#include <patty/ax25/route.h>
|
||||
#include <patty/ax25/sock.h>
|
||||
#include <patty/ax25/server.h>
|
||||
|
|
60
include/patty/ax25/aprs_is.h
Normal file
60
include/patty/ax25/aprs_is.h
Normal file
|
@ -0,0 +1,60 @@
|
|||
#ifndef _PATTY_AX25_APRS_IS_H
|
||||
#define _PATTY_AX25_APRS_IS_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <patty/ax25.h>
|
||||
|
||||
#define PATTY_AX25_APRS_IS_DEFAULT_APPNAME "patty-aprs-is"
|
||||
#define PATTY_AX25_APRS_IS_DEFAULT_VERSION "0.0.0"
|
||||
|
||||
#define PATTY_AX25_APRS_IS_PAYLOAD_MAX 256
|
||||
#define PATTY_AX25_APRS_IS_PACKET_MAX 512
|
||||
#define PATTY_AX25_APRS_IS_FRAME_MAX PATTY_AX25_APRS_IS_PACKET_MAX
|
||||
|
||||
typedef struct _patty_ax25_aprs_is patty_ax25_aprs_is;
|
||||
|
||||
typedef struct _patty_ax25_aprs_is_info {
|
||||
const char *host,
|
||||
*port,
|
||||
*user,
|
||||
*pass,
|
||||
*appname,
|
||||
*version,
|
||||
*filter;
|
||||
} patty_ax25_aprs_is_info;
|
||||
|
||||
ssize_t patty_ax25_aprs_is_encode(void *dest,
|
||||
const char *src,
|
||||
size_t len,
|
||||
size_t size);
|
||||
|
||||
patty_ax25_aprs_is *patty_ax25_aprs_is_new(patty_ax25_aprs_is_info *info);
|
||||
|
||||
void patty_ax25_aprs_is_destroy(patty_ax25_aprs_is *aprs);
|
||||
|
||||
ssize_t patty_ax25_aprs_is_readline(patty_ax25_aprs_is *aprs,
|
||||
void *buf,
|
||||
size_t len);
|
||||
|
||||
int patty_ax25_aprs_is_printf(patty_ax25_aprs_is *aprs, const char *fmt, ...);
|
||||
|
||||
int patty_ax25_aprs_is_fd(patty_ax25_aprs_is *aprs);
|
||||
|
||||
ssize_t patty_ax25_aprs_is_pending(patty_ax25_aprs_is *aprs);
|
||||
|
||||
ssize_t patty_ax25_aprs_is_fill(patty_ax25_aprs_is *aprs);
|
||||
|
||||
ssize_t patty_ax25_aprs_is_drain(patty_ax25_aprs_is *, void *buf, size_t len);
|
||||
|
||||
int patty_ax25_aprs_is_ready(patty_ax25_aprs_is *);
|
||||
|
||||
ssize_t patty_ax25_aprs_is_flush(patty_ax25_aprs_is *);
|
||||
|
||||
ssize_t patty_ax25_aprs_is_send(patty_ax25_aprs_is *aprs,
|
||||
const void *buf,
|
||||
size_t len);
|
||||
|
||||
patty_ax25_if_driver *patty_ax25_aprs_is_driver();
|
||||
|
||||
#endif /* _PATTY_AX25_APRS_IS_H */
|
|
@ -7,12 +7,12 @@ CC = $(CROSS)cc
|
|||
CFLAGS = $(CGFLAGS) -fPIC -Wall -O2 -I$(INCLUDE_PATH)
|
||||
LDFLAGS = -lutil
|
||||
|
||||
HEADERS = kiss.h kiss/tnc.h ax25.h client.h ax25/if.h ax25/frame.h \
|
||||
ax25/sock.h ax25/route.h ax25/server.h daemon.h \
|
||||
HEADERS = kiss.h kiss/tnc.h ax25/aprs_is.h ax25.h client.h ax25/if.h \
|
||||
ax25/frame.h ax25/sock.h ax25/route.h ax25/server.h daemon.h \
|
||||
error.h list.h hash.h dict.h timer.h print.h conf.h
|
||||
|
||||
OBJS = kiss.o tnc.o ax25.o client.o if.o frame.o \
|
||||
sock.o route.o server.o daemon.o \
|
||||
OBJS = kiss.o tnc.o aprs_is.o ax25.o client.o if.o \
|
||||
frame.o sock.o route.o server.o daemon.o \
|
||||
error.o list.o hash.o dict.o timer.o print.o conf.o
|
||||
|
||||
VERSION_MAJOR = 0
|
||||
|
|
349
src/aprs_is.c
Normal file
349
src/aprs_is.c
Normal file
|
@ -0,0 +1,349 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <netdb.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
|
||||
#include <patty/ax25/aprs_is.h>
|
||||
|
||||
enum state {
|
||||
APRS_IS_HEADER,
|
||||
APRS_IS_COMMENT,
|
||||
APRS_IS_BODY,
|
||||
APRS_IS_COMPLETE
|
||||
};
|
||||
|
||||
struct _patty_ax25_aprs_is {
|
||||
int fd;
|
||||
|
||||
char rx_buf[PATTY_AX25_APRS_IS_PACKET_MAX],
|
||||
tx_buf[PATTY_AX25_APRS_IS_FRAME_MAX],
|
||||
station[PATTY_AX25_ADDRSTRLEN+1],
|
||||
info[PATTY_AX25_APRS_IS_PAYLOAD_MAX];
|
||||
|
||||
patty_ax25_frame frame;
|
||||
|
||||
size_t rx_bufsz,
|
||||
tx_bufsz,
|
||||
infosz;
|
||||
|
||||
enum state state;
|
||||
|
||||
size_t offset_i,
|
||||
offset_station,
|
||||
offset_info;
|
||||
|
||||
ssize_t readlen,
|
||||
encoded;
|
||||
};
|
||||
|
||||
static ssize_t send_line(patty_ax25_aprs_is *aprs, char *buf, size_t len) {
|
||||
if (len > PATTY_AX25_APRS_IS_PACKET_MAX - 2) {
|
||||
len = PATTY_AX25_APRS_IS_PACKET_MAX - 2;
|
||||
}
|
||||
|
||||
buf[len] = '\r';
|
||||
buf[len+1] = '\n';
|
||||
|
||||
return write(aprs->fd, buf, len+2);
|
||||
}
|
||||
|
||||
static int send_va(patty_ax25_aprs_is *aprs, const char *fmt, va_list args) {
|
||||
size_t len;
|
||||
|
||||
if (vsnprintf(aprs->tx_buf, PATTY_AX25_APRS_IS_PACKET_MAX, fmt, args) < 0) {
|
||||
goto error_vsnprintf;
|
||||
}
|
||||
|
||||
len = strnlen(aprs->tx_buf, PATTY_AX25_APRS_IS_PACKET_MAX);
|
||||
|
||||
if (send_line(aprs, aprs->tx_buf, len) < 0) {
|
||||
goto error_send_line;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
error_send_line:
|
||||
error_vsnprintf:
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int send_fmt(patty_ax25_aprs_is *aprs, const char *fmt, ...) {
|
||||
va_list args;
|
||||
|
||||
va_start(args, fmt);
|
||||
|
||||
if (send_va(aprs, fmt, args) < 0) {
|
||||
goto error_send_va;
|
||||
}
|
||||
|
||||
va_end(args);
|
||||
|
||||
return 0;
|
||||
|
||||
error_send_va:
|
||||
return -1;
|
||||
}
|
||||
|
||||
patty_ax25_aprs_is *patty_ax25_aprs_is_new(patty_ax25_aprs_is_info *info) {
|
||||
patty_ax25_aprs_is *aprs;
|
||||
|
||||
struct addrinfo *ai0,
|
||||
*ai;
|
||||
|
||||
if (getaddrinfo(info->host, info->port, NULL, &ai0) < 0) {
|
||||
goto error_getaddrinfo;
|
||||
}
|
||||
|
||||
if ((aprs = malloc(sizeof(*aprs))) == NULL) {
|
||||
goto error_malloc_aprs;
|
||||
}
|
||||
|
||||
memset(aprs, '\0', sizeof(*aprs));
|
||||
|
||||
aprs->rx_bufsz = PATTY_AX25_APRS_IS_PACKET_MAX;
|
||||
aprs->tx_bufsz = PATTY_AX25_APRS_IS_FRAME_MAX;
|
||||
aprs->infosz = PATTY_AX25_APRS_IS_PAYLOAD_MAX;
|
||||
aprs->state = APRS_IS_HEADER;
|
||||
|
||||
for (ai=ai0; ai; ai=ai->ai_next) {
|
||||
if ((aprs->fd = socket(ai->ai_family,
|
||||
ai->ai_socktype,
|
||||
ai->ai_protocol)) < 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (connect(aprs->fd, ai->ai_addr, ai->ai_addrlen) < 0) {
|
||||
close(aprs->fd);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
freeaddrinfo(ai0);
|
||||
|
||||
if (send_fmt(aprs,
|
||||
"user %s pass %s vers %s %s filter %s",
|
||||
info->user,
|
||||
info->pass,
|
||||
info->appname,
|
||||
info->version,
|
||||
info->filter) < 0) {
|
||||
goto error_send_fmt;
|
||||
}
|
||||
|
||||
return aprs;
|
||||
}
|
||||
|
||||
error_send_fmt:
|
||||
close(aprs->fd);
|
||||
free(aprs);
|
||||
|
||||
error_malloc_aprs:
|
||||
error_getaddrinfo:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void patty_ax25_aprs_is_destroy(patty_ax25_aprs_is *aprs) {
|
||||
(void)close(aprs->fd);
|
||||
|
||||
free(aprs);
|
||||
}
|
||||
|
||||
int patty_ax25_aprs_is_fd(patty_ax25_aprs_is *aprs) {
|
||||
return aprs->fd;
|
||||
}
|
||||
|
||||
ssize_t patty_ax25_aprs_is_pending(patty_ax25_aprs_is *aprs) {
|
||||
return aprs->offset_i < aprs->readlen? 1: 0;
|
||||
}
|
||||
|
||||
ssize_t patty_ax25_aprs_is_fill(patty_ax25_aprs_is *aprs) {
|
||||
if ((aprs->readlen = read(aprs->fd, aprs->rx_buf, aprs->rx_bufsz)) < 0) {
|
||||
goto error_read;
|
||||
}
|
||||
|
||||
aprs->offset_i = 0;
|
||||
|
||||
return aprs->readlen;
|
||||
|
||||
error_read:
|
||||
return -1;
|
||||
}
|
||||
|
||||
ssize_t patty_ax25_aprs_is_drain(patty_ax25_aprs_is *aprs,
|
||||
void *buf,
|
||||
size_t len) {
|
||||
ssize_t offset_start = aprs->offset_i;
|
||||
|
||||
while (aprs->offset_i < aprs->readlen) {
|
||||
char c = aprs->rx_buf[aprs->offset_i++];
|
||||
|
||||
switch (aprs->state) {
|
||||
case APRS_IS_HEADER: {
|
||||
patty_ax25_addr *addr = NULL;
|
||||
|
||||
if (c == '#') {
|
||||
aprs->state = APRS_IS_COMMENT;
|
||||
} else if (c == '>') {
|
||||
addr = &aprs->frame.src;
|
||||
} else if (c == ',' || c == ':') {
|
||||
if (aprs->frame.dest.callsign[0]) {
|
||||
if (aprs->frame.hops == PATTY_AX25_MAX_HOPS) {
|
||||
errno = EOVERFLOW;
|
||||
|
||||
goto error;
|
||||
}
|
||||
|
||||
addr = &aprs->frame.repeaters[aprs->frame.hops++];
|
||||
} else {
|
||||
addr = &aprs->frame.dest;
|
||||
}
|
||||
|
||||
if (c == ':') {
|
||||
aprs->state = APRS_IS_BODY;
|
||||
}
|
||||
} else if (PATTY_AX25_ADDR_CHAR_VALID(c)) {
|
||||
if (aprs->offset_station == PATTY_AX25_ADDRSTRLEN) {
|
||||
errno = EOVERFLOW;
|
||||
|
||||
goto error;
|
||||
}
|
||||
|
||||
aprs->station[aprs->offset_station++] = c;
|
||||
} else {
|
||||
errno = EINVAL;
|
||||
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (addr) {
|
||||
aprs->station[aprs->offset_station] = '\0';
|
||||
|
||||
if (patty_ax25_pton(aprs->station, addr) < 0) {
|
||||
(void)patty_ax25_aprs_is_flush(aprs);
|
||||
|
||||
goto done;
|
||||
}
|
||||
|
||||
aprs->offset_station = 0;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case APRS_IS_COMMENT:
|
||||
if (c == '\r') {
|
||||
break;
|
||||
} else if (c == '\n') {
|
||||
aprs->state = APRS_IS_HEADER;
|
||||
|
||||
goto done;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case APRS_IS_BODY:
|
||||
if (c == '\r') {
|
||||
break;
|
||||
} else if (c == '\n') {
|
||||
aprs->state = APRS_IS_COMPLETE;
|
||||
|
||||
aprs->frame.control = PATTY_AX25_FRAME_UI;
|
||||
aprs->frame.cr = PATTY_AX25_FRAME_COMMAND;
|
||||
aprs->frame.proto = PATTY_AX25_PROTO_NONE;
|
||||
aprs->frame.info = aprs->info;
|
||||
aprs->frame.infolen = aprs->offset_info;
|
||||
|
||||
if ((aprs->encoded = patty_ax25_frame_encode(&aprs->frame,
|
||||
buf,
|
||||
len)) < 0) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
goto done;
|
||||
} else {
|
||||
if (aprs->offset_info == aprs->infosz) {
|
||||
errno = EOVERFLOW;
|
||||
|
||||
goto error;
|
||||
}
|
||||
|
||||
aprs->info[aprs->offset_info++] = c;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case APRS_IS_COMPLETE:
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
done:
|
||||
return aprs->offset_i - offset_start;
|
||||
|
||||
error:
|
||||
return -1;
|
||||
}
|
||||
|
||||
int patty_ax25_aprs_is_ready(patty_ax25_aprs_is *aprs) {
|
||||
return aprs->state == APRS_IS_COMPLETE? 1: 0;
|
||||
}
|
||||
|
||||
ssize_t patty_ax25_aprs_is_flush(patty_ax25_aprs_is *aprs) {
|
||||
ssize_t ret = aprs->encoded;
|
||||
|
||||
aprs->state = APRS_IS_HEADER;
|
||||
aprs->offset_i = aprs->readlen;
|
||||
aprs->offset_station = 0;
|
||||
aprs->offset_info = 0;
|
||||
aprs->encoded = 0;
|
||||
|
||||
memset(&aprs->frame, '\0', sizeof(aprs->frame));
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int patty_ax25_aprs_is_printf(patty_ax25_aprs_is *aprs, const char *fmt, ...) {
|
||||
va_list args;
|
||||
|
||||
va_start(args, fmt);
|
||||
|
||||
if (send_va(aprs, fmt, args) < 0) {
|
||||
goto error_send_va;
|
||||
}
|
||||
|
||||
va_end(args);
|
||||
|
||||
return 0;
|
||||
|
||||
error_send_va:
|
||||
va_end(args);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
ssize_t patty_ax25_aprs_is_send(patty_ax25_aprs_is *aprs,
|
||||
const void *buf,
|
||||
size_t len) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
patty_ax25_if_driver *patty_ax25_aprs_is_driver() {
|
||||
static patty_ax25_if_driver driver = {
|
||||
.create = (patty_ax25_if_driver_create *)patty_ax25_aprs_is_new,
|
||||
.destroy = (patty_ax25_if_driver_destroy *)patty_ax25_aprs_is_destroy,
|
||||
.fd = (patty_ax25_if_driver_fd *)patty_ax25_aprs_is_fd,
|
||||
.pending = (patty_ax25_if_driver_pending *)patty_ax25_aprs_is_pending,
|
||||
.fill = (patty_ax25_if_driver_fill *)patty_ax25_aprs_is_fill,
|
||||
.drain = (patty_ax25_if_driver_drain *)patty_ax25_aprs_is_drain,
|
||||
.ready = (patty_ax25_if_driver_ready *)patty_ax25_aprs_is_ready,
|
||||
.flush = (patty_ax25_if_driver_flush *)patty_ax25_aprs_is_flush,
|
||||
.send = (patty_ax25_if_driver_send *)patty_ax25_aprs_is_send
|
||||
};
|
||||
|
||||
return &driver;
|
||||
};
|
Loading…
Add table
Reference in a new issue