Implement patty_ax25_if_driver
Changes: * Implement patty_ax25_if_driver type, providing a vtable with pointers to methods implementing an AX.25 interface PHY * Implement patty_ax25_if_name() to return a pointer to the name string of an AX.25 interface * Decouple patty_kiss_tnc from src/if.c using patty_ax25_if_driver * Remove port input/output arguments from patty_kiss_tnc_send() and patty_kiss_tnc_recv(), respectively; use 0 as the default, but keep the port argument in patty_kiss_frame_send() * Implement patty_ax25_if_fd() to return file descriptor backing a PHY; use this rather than patty_kiss_tnc_fd() in src/server.c to further decouple interfaces from their implementation * Remove 'enum patty_ax25_if_type' type; refactor constructor patty_ax25_if_new() to no longer take this as an argument, but rather a patty_ax25_if_driver to use to instantiate the PHY with the information pointer passed * Break out patty_kiss_tnc code from src/kiss.c into src/tnc.c, leaving only patty_kiss_frame_send() in the original; this is needed to prevent a cyclic dependency within patty_ax25_sock and other areas * Rename patty_bin_if_config() to patty_bin_if_create(); a separate patty_bin_if_config() will likely be created later as necessary to change an existing interface * Split PHY-specific interface configuration code into separate delegates in bin/if.c * Implement usage of patty_error to better capture internal error states not currently representable with semantic error numbers while configuring and instantiating PHYs * Pass patty_error object to patty_bin_kiss_config() to receive detailed error information
This commit is contained in:
parent
356b25fd94
commit
cb33d799ff
16 changed files with 669 additions and 586 deletions
|
@ -35,7 +35,7 @@ static void usage(int argc, char **argv, const char *message, ...) {
|
|||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
patty_client *client;
|
||||
patty_client *client = NULL;
|
||||
|
||||
struct option opts[] = {
|
||||
{ NULL, 0, NULL, 0 }
|
||||
|
@ -44,8 +44,7 @@ int main(int argc, char **argv) {
|
|||
uint8_t buf[4096];
|
||||
ssize_t readlen;
|
||||
|
||||
patty_bin_kiss_data data;
|
||||
patty_kiss_tnc_info *info = &data.info;
|
||||
patty_kiss_tnc_info info;
|
||||
patty_kiss_tnc *raw;
|
||||
|
||||
struct stat st;
|
||||
|
@ -66,7 +65,7 @@ int main(int argc, char **argv) {
|
|||
goto error_stat;
|
||||
}
|
||||
|
||||
memset(&data, '\0', sizeof(data));
|
||||
memset(&info, '\0', sizeof(info));
|
||||
|
||||
if ((st.st_mode & S_IFMT) == S_IFSOCK) {
|
||||
patty_client_setsockopt_if ifreq;
|
||||
|
@ -75,7 +74,7 @@ int main(int argc, char **argv) {
|
|||
usage(argc, argv, "No interface name provided");
|
||||
}
|
||||
|
||||
info->flags = PATTY_KISS_TNC_FD;
|
||||
info.flags = PATTY_KISS_TNC_FD;
|
||||
|
||||
if ((client = patty_client_new(argv[1])) == NULL) {
|
||||
fprintf(stderr, "%s: %s: %s: %s\n",
|
||||
|
@ -84,7 +83,7 @@ int main(int argc, char **argv) {
|
|||
goto error_client_new;
|
||||
}
|
||||
|
||||
if ((info->fd = patty_client_socket(client, PATTY_AX25_PROTO_NONE, PATTY_AX25_SOCK_RAW)) < 0) {
|
||||
if ((info.fd = patty_client_socket(client, PATTY_AX25_PROTO_NONE, PATTY_AX25_SOCK_RAW)) < 0) {
|
||||
fprintf(stderr, "%s: %s: %s\n",
|
||||
argv[0], "patty_client_socket()", strerror(errno));
|
||||
|
||||
|
@ -95,29 +94,31 @@ int main(int argc, char **argv) {
|
|||
|
||||
ifreq.state = PATTY_AX25_SOCK_PROMISC;
|
||||
|
||||
if (patty_client_setsockopt(client, info->fd, PATTY_AX25_SOCK_IF, &ifreq, sizeof(ifreq)) < 0) {
|
||||
if (patty_client_setsockopt(client, info.fd, PATTY_AX25_SOCK_IF, &ifreq, sizeof(ifreq)) < 0) {
|
||||
fprintf(stderr, "%s: %s: %s\n",
|
||||
argv[0], "patty_client_setsockopt()", strerror(errno));
|
||||
|
||||
goto error_client_setsockopt;
|
||||
}
|
||||
} else {
|
||||
if (patty_bin_kiss_config(&data, argc - 1, argv + 1) < 0) {
|
||||
patty_error e;
|
||||
|
||||
if (patty_bin_kiss_config(argc - 1, argv + 1, &info, &e) < 0) {
|
||||
fprintf(stderr, "%s: %s: %s\n",
|
||||
argv[0], argv[1], data.err);
|
||||
argv[0], argv[1], patty_error_string(&e));
|
||||
|
||||
goto error_kiss_config;
|
||||
}
|
||||
}
|
||||
|
||||
if ((raw = patty_kiss_tnc_new(info)) == NULL) {
|
||||
if ((raw = patty_kiss_tnc_new(&info)) == NULL) {
|
||||
fprintf(stderr, "%s: fd %d: %s: %s\n",
|
||||
argv[0], info->fd, "patty_kiss_tnc_new()", strerror(errno));
|
||||
argv[0], info.fd, "patty_kiss_tnc_new()", strerror(errno));
|
||||
|
||||
goto error_kiss_tnc_new;
|
||||
}
|
||||
|
||||
while ((readlen = patty_kiss_tnc_recv(raw, buf, sizeof(buf), NULL)) > 0) {
|
||||
while ((readlen = patty_kiss_tnc_recv(raw, buf, sizeof(buf))) > 0) {
|
||||
ssize_t decoded,
|
||||
offset = 0;
|
||||
|
||||
|
@ -178,7 +179,7 @@ error_ax25_frame_decode_address:
|
|||
patty_kiss_tnc_destroy(raw);
|
||||
|
||||
if ((st.st_mode & S_IFMT) == S_IFSOCK) {
|
||||
patty_client_close(client, info->fd);
|
||||
patty_client_close(client, info.fd);
|
||||
|
||||
patty_client_destroy(client);
|
||||
}
|
||||
|
@ -192,10 +193,10 @@ error_kiss_tnc_new:
|
|||
error_kiss_config:
|
||||
error_client_setsockopt:
|
||||
error_client_socket:
|
||||
if ((st.st_mode & S_IFMT) == S_IFSOCK) patty_client_close(client, info->fd);
|
||||
if (client) patty_client_close(client, info.fd);
|
||||
|
||||
error_client_new:
|
||||
if ((st.st_mode & S_IFMT) == S_IFSOCK) patty_client_destroy(client);
|
||||
if (client) patty_client_destroy(client);
|
||||
|
||||
error_stat:
|
||||
return 1;
|
||||
|
|
195
bin/if.c
195
bin/if.c
|
@ -6,6 +6,13 @@
|
|||
|
||||
#include <patty/bin/if.h>
|
||||
|
||||
struct context {
|
||||
patty_error *err;
|
||||
|
||||
char *name;
|
||||
patty_ax25_addr addr;
|
||||
};
|
||||
|
||||
enum mode {
|
||||
MODE_NONE,
|
||||
MODE_IFNAME,
|
||||
|
@ -16,30 +23,119 @@ enum mode {
|
|||
MODE_FLOW
|
||||
};
|
||||
|
||||
static int eprintf(patty_bin_if_data *data, const char *format, ...) {
|
||||
int ret;
|
||||
va_list args;
|
||||
static patty_ax25_if *create_kiss(struct context *ctx, int argc, char **argv) {
|
||||
enum mode mode = MODE_KISS;
|
||||
|
||||
va_start(args, format);
|
||||
|
||||
ret = vsnprintf(data->err, sizeof(data->err)-1, format, args);
|
||||
|
||||
va_end(args);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int patty_bin_if_config(patty_bin_if_data *data, int argc, char **argv) {
|
||||
enum mode mode = MODE_NONE;
|
||||
patty_kiss_tnc_info *info = &data->info;
|
||||
patty_kiss_tnc_info info;
|
||||
|
||||
int i;
|
||||
|
||||
memset(&info, '\0', sizeof(info));
|
||||
|
||||
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_KISS:
|
||||
info.flags |= PATTY_KISS_TNC_DEVICE;
|
||||
info.device = argv[i];
|
||||
|
||||
mode = MODE_IFOPTS;
|
||||
|
||||
break;
|
||||
|
||||
case MODE_IFOPTS:
|
||||
if (strcmp(argv[i], "baud") == 0) {
|
||||
mode = MODE_BAUD;
|
||||
} else if (strcmp(argv[i], "flow") == 0) {
|
||||
mode = MODE_FLOW;
|
||||
} else {
|
||||
patty_error_fmt(ctx->err, "Invalid parameter '%s'",
|
||||
argv[i]);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case MODE_BAUD:
|
||||
if (!(argv[i][0] >= '0' && argv[i][0] <= '9')) {
|
||||
patty_error_fmt(ctx->err, "Invalid baud rate '%s'",
|
||||
argv[i]);
|
||||
|
||||
goto error_invalid;
|
||||
}
|
||||
|
||||
info.flags |= PATTY_KISS_TNC_BAUD;
|
||||
info.baud = atoi(argv[i]);
|
||||
|
||||
mode = MODE_IFOPTS;
|
||||
|
||||
break;
|
||||
|
||||
case MODE_FLOW:
|
||||
if (strcmp(argv[i], "crtscts") == 0) {
|
||||
info.flags |= PATTY_KISS_TNC_FLOW;
|
||||
info.flow = PATTY_KISS_TNC_FLOW_CRTSCTS;
|
||||
} else if (strcmp(argv[i], "xonxoff") == 0) {
|
||||
info.flags |= PATTY_KISS_TNC_FLOW;
|
||||
info.flow = PATTY_KISS_TNC_FLOW_XONXOFF;
|
||||
} else {
|
||||
patty_error_fmt(ctx->err, "Invalid flow control '%s'",
|
||||
argv[i]);
|
||||
|
||||
goto error_invalid;
|
||||
}
|
||||
|
||||
mode = MODE_IFOPTS;
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return patty_ax25_if_new( ctx->name,
|
||||
&ctx->addr,
|
||||
patty_kiss_tnc_driver(),
|
||||
&info);
|
||||
|
||||
error_invalid:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct if_type {
|
||||
const char *name;
|
||||
patty_ax25_if *(*func)(struct context *ctx, int argc, char **argv);
|
||||
};
|
||||
|
||||
struct if_type if_types[] = {
|
||||
{ "kiss", create_kiss },
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
||||
patty_ax25_if *patty_bin_if_create(int argc, char **argv, patty_error *e) {
|
||||
enum mode mode = MODE_NONE;
|
||||
|
||||
struct context ctx = {
|
||||
.err = e,
|
||||
.name = NULL
|
||||
};
|
||||
|
||||
int i;
|
||||
|
||||
patty_error_clear(e);
|
||||
|
||||
memset(&ctx.addr, '\0', sizeof(ctx.addr));
|
||||
|
||||
for (i=0; i<argc; i++) {
|
||||
switch (mode) {
|
||||
case MODE_NONE:
|
||||
if (strcmp(argv[i], "if") != 0) {
|
||||
eprintf(data, "Unexpected start of expression '%s'",
|
||||
patty_error_fmt(e, "Unexpected start of expression '%s'",
|
||||
argv[i]);
|
||||
|
||||
goto error_invalid;
|
||||
|
@ -50,7 +146,7 @@ int patty_bin_if_config(patty_bin_if_data *data, int argc, char **argv) {
|
|||
break;
|
||||
|
||||
case MODE_IFNAME:
|
||||
data->name = argv[i];
|
||||
ctx.name = argv[i];
|
||||
|
||||
mode = MODE_IFOPTS;
|
||||
|
||||
|
@ -59,14 +155,18 @@ int patty_bin_if_config(patty_bin_if_data *data, int argc, char **argv) {
|
|||
case MODE_IFOPTS:
|
||||
if (strcmp(argv[i], "ax25") == 0) {
|
||||
mode = MODE_IFADDR;
|
||||
} else if (strcmp(argv[i], "kiss") == 0) {
|
||||
mode = MODE_KISS;
|
||||
} else if (strcmp(argv[i], "baud") == 0) {
|
||||
mode = MODE_BAUD;
|
||||
} else if (strcmp(argv[i], "flow") == 0) {
|
||||
mode = MODE_FLOW;
|
||||
} else {
|
||||
eprintf(data, "Invalid parameter '%s'", argv[i]);
|
||||
int t;
|
||||
|
||||
for (t=0; if_types[t].name; t++) {
|
||||
if (strcmp(if_types[t].name, argv[i]) == 0) {
|
||||
return if_types[t].func(&ctx,
|
||||
argc - i - 1,
|
||||
argv + i + 1);
|
||||
}
|
||||
}
|
||||
|
||||
patty_error_fmt(e, "Invalid parameter '%s'", argv[i]);
|
||||
|
||||
goto error_invalid;
|
||||
}
|
||||
|
@ -74,8 +174,8 @@ int patty_bin_if_config(patty_bin_if_data *data, int argc, char **argv) {
|
|||
break;
|
||||
|
||||
case MODE_IFADDR:
|
||||
if (patty_ax25_pton(argv[i], &data->addr) < 0) {
|
||||
eprintf(data, "Invalid AX.25 address '%s': %s",
|
||||
if (patty_ax25_pton(argv[i], &ctx.addr) < 0) {
|
||||
patty_error_fmt(e, "Invalid AX.25 address '%s': %s",
|
||||
argv[i], strerror(errno));
|
||||
|
||||
goto error_invalid;
|
||||
|
@ -85,50 +185,13 @@ int patty_bin_if_config(patty_bin_if_data *data, int argc, char **argv) {
|
|||
|
||||
break;
|
||||
|
||||
case MODE_KISS:
|
||||
data->type = PATTY_AX25_IF_KISS_TNC;
|
||||
info->flags |= PATTY_KISS_TNC_DEVICE;
|
||||
info->device = argv[i];
|
||||
|
||||
mode = MODE_IFOPTS;
|
||||
|
||||
break;
|
||||
|
||||
case MODE_BAUD:
|
||||
if (!(argv[i][0] >= '0' && argv[i][0] <= '9')) {
|
||||
eprintf(data, "Invalid baud rate '%s'", argv[i]);
|
||||
|
||||
goto error_invalid;
|
||||
}
|
||||
|
||||
info->flags |= PATTY_KISS_TNC_BAUD;
|
||||
info->baud = atoi(argv[i]);
|
||||
|
||||
mode = MODE_IFOPTS;
|
||||
|
||||
break;
|
||||
|
||||
case MODE_FLOW:
|
||||
if (strcmp(argv[i], "crtscts") == 0) {
|
||||
info->flags |= PATTY_KISS_TNC_FLOW;
|
||||
info->flow = PATTY_KISS_TNC_FLOW_CRTSCTS;
|
||||
} else if (strcmp(argv[i], "xonxoff") == 0) {
|
||||
info->flags |= PATTY_KISS_TNC_FLOW;
|
||||
info->flow = PATTY_KISS_TNC_FLOW_XONXOFF;
|
||||
} else {
|
||||
eprintf(data, "Invalid flow control '%s'", argv[i]);
|
||||
|
||||
goto error_invalid;
|
||||
}
|
||||
|
||||
mode = MODE_IFOPTS;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
patty_error_fmt(e, "Media type not provided");
|
||||
|
||||
error_invalid:
|
||||
return -1;
|
||||
return NULL;
|
||||
}
|
||||
|
|
11
bin/kiss.c
11
bin/kiss.c
|
@ -4,10 +4,13 @@
|
|||
|
||||
#include <patty/bin/kiss.h>
|
||||
|
||||
int patty_bin_kiss_config(patty_bin_kiss_data *data, int argc, char **argv) {
|
||||
int patty_bin_kiss_config(int argc,
|
||||
char **argv,
|
||||
patty_kiss_tnc_info *info,
|
||||
patty_error *e) {
|
||||
int i;
|
||||
|
||||
patty_kiss_tnc_info *info = &data->info;
|
||||
memset(info, '\0', sizeof(*info));
|
||||
|
||||
info->flags |= PATTY_KISS_TNC_DEVICE;
|
||||
info->device = argv[0];
|
||||
|
@ -25,8 +28,8 @@ int patty_bin_kiss_config(patty_bin_kiss_data *data, int argc, char **argv) {
|
|||
info->flags |= PATTY_KISS_TNC_BAUD;
|
||||
info->baud = baud;
|
||||
} else {
|
||||
snprintf(data->err, sizeof(data->err)-1,
|
||||
"Invalid KISS TNC device parameter '%s'", argv[i]);
|
||||
patty_error_fmt(e, "Invalid KISS TNC device parameter '%s'",
|
||||
argv[i]);
|
||||
|
||||
goto error_invalid_device_setting;
|
||||
}
|
||||
|
|
45
bin/pattyd.c
45
bin/pattyd.c
|
@ -94,10 +94,8 @@ static int handle_if(struct context *ctx,
|
|||
int lineno,
|
||||
int argc,
|
||||
char **argv) {
|
||||
patty_bin_if_data data;
|
||||
patty_ax25_if *iface;
|
||||
|
||||
memset(&data, '\0', sizeof(data));
|
||||
patty_error e;
|
||||
|
||||
if (argc < 2) {
|
||||
fprintf(stderr, "%s: line %d: No interface name provided\n",
|
||||
|
@ -111,30 +109,27 @@ static int handle_if(struct context *ctx,
|
|||
goto error_invalid;
|
||||
}
|
||||
|
||||
if (patty_bin_if_config(&data, argc, argv) < 0) {
|
||||
if ((iface = patty_bin_if_create(argc, argv, &e)) == NULL) {
|
||||
fprintf(stderr, "%s: line %d: %s\n",
|
||||
ctx->config_file, lineno, data.err);
|
||||
ctx->config_file, lineno, patty_error_string(&e));
|
||||
|
||||
goto error_invalid;
|
||||
}
|
||||
|
||||
if ((iface = patty_ax25_if_new(PATTY_AX25_IF_KISS_TNC,
|
||||
data.name,
|
||||
&data.info,
|
||||
&data.addr)) == NULL) {
|
||||
goto error_if_new;
|
||||
} else {
|
||||
int fd = patty_kiss_tnc_fd(iface->tnc);
|
||||
int fd = patty_ax25_if_fd(iface);
|
||||
char *pty;
|
||||
|
||||
if (isatty(fd) && (pty = ptsname(fd)) != NULL) {
|
||||
printf("if %s pty %s\n", data.name, pty);
|
||||
printf("if %s pty %s\n",
|
||||
patty_ax25_if_name(iface), pty);
|
||||
}
|
||||
}
|
||||
|
||||
if (patty_daemon_if_add(ctx->daemon, iface) < 0) {
|
||||
fprintf(stderr, "%s: line %d: Unable to create interface %s: %s\n",
|
||||
ctx->config_file, lineno, data.name, strerror(errno));
|
||||
ctx->config_file,
|
||||
lineno,
|
||||
patty_ax25_if_name(iface),
|
||||
strerror(errno));
|
||||
|
||||
goto error_daemon_if_add;
|
||||
}
|
||||
|
@ -144,7 +139,6 @@ static int handle_if(struct context *ctx,
|
|||
error_daemon_if_add:
|
||||
patty_ax25_if_destroy(iface);
|
||||
|
||||
error_if_new:
|
||||
error_invalid:
|
||||
return -1;
|
||||
}
|
||||
|
@ -281,9 +275,8 @@ static int handle_standalone(patty_daemon *daemon,
|
|||
patty_ax25_if *iface;
|
||||
patty_ax25_addr addr;
|
||||
|
||||
patty_bin_kiss_data data;
|
||||
|
||||
memset(&data, '\0', sizeof(data));
|
||||
patty_kiss_tnc_info info;
|
||||
patty_error e;
|
||||
|
||||
if (patty_daemon_set_sock_path(daemon, argv[0]) < 0) {
|
||||
fprintf(stderr, "%s: Unable to set socket path to %s: %s\n",
|
||||
|
@ -299,23 +292,23 @@ static int handle_standalone(patty_daemon *daemon,
|
|||
goto error_invalid_callsign;
|
||||
}
|
||||
|
||||
if (patty_bin_kiss_config(&data, argc - 2, argv + 2) < 0) {
|
||||
if (patty_bin_kiss_config(argc - 2, argv + 2, &info, &e) < 0) {
|
||||
fprintf(stderr, "%s: %s: %s\n",
|
||||
argv0, argv[2], data.err);
|
||||
argv0, argv[2], patty_error_string(&e));
|
||||
|
||||
goto error_kiss_config;
|
||||
}
|
||||
|
||||
if ((iface = patty_ax25_if_new(PATTY_AX25_IF_KISS_TNC,
|
||||
DEFAULT_IFNAME,
|
||||
&data.info,
|
||||
&addr)) == NULL) {
|
||||
if ((iface = patty_ax25_if_new(DEFAULT_IFNAME,
|
||||
&addr,
|
||||
patty_kiss_tnc_driver(),
|
||||
&info)) == NULL) {
|
||||
fprintf(stderr, "%s: Unable to create network interface %s: %s\n",
|
||||
argv0, DEFAULT_IFNAME, strerror(errno));
|
||||
|
||||
goto error_if_new;
|
||||
} else {
|
||||
int fd = patty_kiss_tnc_fd(iface->tnc);
|
||||
int fd = patty_ax25_if_fd(iface);
|
||||
char *pty;
|
||||
|
||||
if (isatty(fd) && (pty = ptsname(fd)) != NULL) {
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
#include <sys/types.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
#include <patty/kiss.h>
|
||||
#include <patty/list.h>
|
||||
#include <patty/dict.h>
|
||||
|
||||
|
@ -104,6 +103,7 @@ typedef struct _patty_ax25_if patty_ax25_if;
|
|||
#include <patty/client.h>
|
||||
#include <patty/ax25/frame.h>
|
||||
#include <patty/ax25/if.h>
|
||||
#include <patty/kiss/tnc.h>
|
||||
#include <patty/ax25/route.h>
|
||||
#include <patty/ax25/sock.h>
|
||||
#include <patty/ax25/server.h>
|
||||
|
|
|
@ -12,12 +12,6 @@
|
|||
#define PATTY_AX25_IF_DEFAULT_MTU 4096
|
||||
#define PATTY_AX25_IF_DEFAULT_MRU 4096
|
||||
|
||||
enum patty_ax25_if_type {
|
||||
PATTY_AX25_IF_UNKNOWN,
|
||||
PATTY_AX25_IF_KISS_TNC,
|
||||
PATTY_AX25_IF_HDLC
|
||||
};
|
||||
|
||||
enum patty_ax25_if_flags {
|
||||
PATTY_AX25_IF_HALF_DUPLEX = (1 << 0),
|
||||
PATTY_AX25_IF_FULL_DUPLEX = (1 << 1),
|
||||
|
@ -33,9 +27,33 @@ typedef struct _patty_ax25_if_stats {
|
|||
dropped;
|
||||
} patty_ax25_if_stats;
|
||||
|
||||
typedef void *(patty_ax25_if_driver_create)(void *);
|
||||
|
||||
typedef void (patty_ax25_if_driver_destroy)(void *);
|
||||
|
||||
typedef int (patty_ax25_if_driver_fd)(void *);
|
||||
|
||||
typedef int (patty_ax25_if_driver_pending)(void *);
|
||||
|
||||
typedef ssize_t (patty_ax25_if_driver_recv)(void *, void *, size_t);
|
||||
|
||||
typedef ssize_t (patty_ax25_if_driver_send)(void *, const void *, size_t);
|
||||
|
||||
typedef struct _patty_ax25_if_driver {
|
||||
patty_ax25_if_driver_create *create;
|
||||
patty_ax25_if_driver_destroy *destroy;
|
||||
patty_ax25_if_driver_fd *fd;
|
||||
patty_ax25_if_driver_pending *pending;
|
||||
patty_ax25_if_driver_recv *recv;
|
||||
patty_ax25_if_driver_send *send;
|
||||
} patty_ax25_if_driver;
|
||||
|
||||
typedef void patty_ax25_if_phy;
|
||||
|
||||
typedef void patty_ax25_if_info;
|
||||
|
||||
typedef struct _patty_ax25_if {
|
||||
enum patty_ax25_if_type type;
|
||||
patty_ax25_if_stats stats;
|
||||
patty_ax25_if_stats stats;
|
||||
|
||||
char name[8];
|
||||
|
||||
|
@ -47,21 +65,23 @@ typedef struct _patty_ax25_if {
|
|||
size_t mru,
|
||||
mtu;
|
||||
|
||||
patty_kiss_tnc *tnc;
|
||||
patty_ax25_addr addr;
|
||||
patty_list *aliases;
|
||||
patty_dict *promisc_fds;
|
||||
|
||||
patty_ax25_if_driver *driver;
|
||||
patty_ax25_if_phy *phy;
|
||||
} patty_ax25_if;
|
||||
|
||||
typedef void patty_ax25_if_info;
|
||||
|
||||
patty_ax25_if *patty_ax25_if_new(enum patty_ax25_if_type type,
|
||||
const char *name,
|
||||
patty_ax25_if_info *info,
|
||||
patty_ax25_addr *addr);
|
||||
patty_ax25_if *patty_ax25_if_new(const char *name,
|
||||
patty_ax25_addr *addr,
|
||||
patty_ax25_if_driver *driver,
|
||||
patty_ax25_if_info *info);
|
||||
|
||||
void patty_ax25_if_destroy(patty_ax25_if *iface);
|
||||
|
||||
char *patty_ax25_if_name(patty_ax25_if *iface);
|
||||
|
||||
int patty_ax25_if_addr_each(patty_ax25_if *iface,
|
||||
int (*callback)(patty_ax25_addr *, void *),
|
||||
void *ctx);
|
||||
|
@ -78,6 +98,8 @@ int patty_ax25_if_promisc_add(patty_ax25_if *iface,
|
|||
int patty_ax25_if_promisc_delete(patty_ax25_if *iface,
|
||||
int fd);
|
||||
|
||||
int patty_ax25_if_fd(patty_ax25_if *iface);
|
||||
|
||||
ssize_t patty_ax25_if_pending(patty_ax25_if *iface);
|
||||
|
||||
ssize_t patty_ax25_if_recv(patty_ax25_if *iface,
|
||||
|
|
|
@ -123,7 +123,7 @@ typedef struct _patty_ax25_sock {
|
|||
/*
|
||||
* Reader for raw packets for PATTY_AX25_SOCK_RAW type
|
||||
*/
|
||||
patty_kiss_tnc *raw;
|
||||
struct _patty_kiss_tnc *raw;
|
||||
|
||||
/*
|
||||
* File descriptor information
|
||||
|
|
|
@ -2,16 +2,10 @@
|
|||
#define _PATTY_BIN_IF_H
|
||||
|
||||
#include <patty/ax25.h>
|
||||
#include <patty/error.h>
|
||||
|
||||
typedef struct _patty_bin_if_data {
|
||||
char *name;
|
||||
char err[256];
|
||||
|
||||
enum patty_ax25_if_type type;
|
||||
patty_ax25_addr addr;
|
||||
patty_kiss_tnc_info info;
|
||||
} patty_bin_if_data;
|
||||
|
||||
int patty_bin_if_config(patty_bin_if_data *data, int argc, char **argv);
|
||||
patty_ax25_if *patty_bin_if_create(int argc,
|
||||
char **argv,
|
||||
patty_error *e);
|
||||
|
||||
#endif /* _PATTY_BIN_IF_H */
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
#ifndef _PATTY_BIN_KISS_H
|
||||
#define _PATTY_BIN_KISS_H
|
||||
|
||||
#include <patty/kiss.h>
|
||||
#include <patty/error.h>
|
||||
|
||||
typedef struct _patty_bin_kiss_data {
|
||||
char err[256];
|
||||
patty_kiss_tnc_info info;
|
||||
} patty_bin_kiss_data;
|
||||
#include <patty/kiss/tnc.h>
|
||||
|
||||
int patty_bin_kiss_config(patty_bin_kiss_data *data, int argc, char **argv);
|
||||
int patty_bin_kiss_config(int argc,
|
||||
char **argv,
|
||||
patty_kiss_tnc_info *info,
|
||||
patty_error *e);
|
||||
|
||||
#endif /* _PATTY_BIN_KISS_H */
|
||||
|
|
|
@ -2,17 +2,14 @@
|
|||
#define _PATTY_KISS_H
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <termios.h>
|
||||
|
||||
#define PATTY_KISS_FEND 0xc0
|
||||
#define PATTY_KISS_FESC 0xdb
|
||||
#define PATTY_KISS_TFEND 0xdc
|
||||
#define PATTY_KISS_TFESC 0xdd
|
||||
|
||||
#define PATTY_KISS_BUFSZ 4096
|
||||
|
||||
#define PATTY_KISS_COMMAND(cmd) \
|
||||
(cmd & 0x0f)
|
||||
((cmd & 0x0f))
|
||||
|
||||
#define PATTY_KISS_COMMAND_PORT(cmd) \
|
||||
((cmd & 0xf0) >> 4)
|
||||
|
@ -33,46 +30,4 @@ ssize_t patty_kiss_frame_send(int fd,
|
|||
size_t len,
|
||||
int port);
|
||||
|
||||
typedef struct _patty_kiss_tnc patty_kiss_tnc;
|
||||
|
||||
#define PATTY_KISS_TNC_DEVICE (1 << 0)
|
||||
#define PATTY_KISS_TNC_FD (1 << 1)
|
||||
#define PATTY_KISS_TNC_BAUD (1 << 2)
|
||||
#define PATTY_KISS_TNC_FLOW (1 << 3)
|
||||
|
||||
enum patty_kiss_tnc_flow {
|
||||
PATTY_KISS_TNC_FLOW_NONE,
|
||||
PATTY_KISS_TNC_FLOW_CRTSCTS,
|
||||
PATTY_KISS_TNC_FLOW_XONXOFF
|
||||
};
|
||||
|
||||
typedef struct _patty_kiss_tnc_info {
|
||||
int flags;
|
||||
|
||||
const char *device;
|
||||
int fd;
|
||||
speed_t baud;
|
||||
enum patty_kiss_tnc_flow flow;
|
||||
} patty_kiss_tnc_info;
|
||||
|
||||
patty_kiss_tnc *patty_kiss_tnc_new(patty_kiss_tnc_info *info);
|
||||
|
||||
int patty_kiss_tnc_fd(patty_kiss_tnc *tnc);
|
||||
|
||||
void patty_kiss_tnc_destroy(patty_kiss_tnc *tnc);
|
||||
|
||||
size_t patty_kiss_tnc_dropped(patty_kiss_tnc *tnc);
|
||||
|
||||
ssize_t patty_kiss_tnc_pending(patty_kiss_tnc *tnc);
|
||||
|
||||
ssize_t patty_kiss_tnc_recv(patty_kiss_tnc *tnc,
|
||||
void *buf,
|
||||
size_t len,
|
||||
int *port);
|
||||
|
||||
ssize_t patty_kiss_tnc_send(patty_kiss_tnc *tnc,
|
||||
const void *buf,
|
||||
size_t len,
|
||||
int port);
|
||||
|
||||
#endif /* _PATTY_KISS_H */
|
||||
|
|
54
include/patty/kiss/tnc.h
Normal file
54
include/patty/kiss/tnc.h
Normal file
|
@ -0,0 +1,54 @@
|
|||
#ifndef _PATTY_KISS_TNC_H
|
||||
#define _PATTY_KISS_TNC_H
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <termios.h>
|
||||
|
||||
#include <patty/ax25.h>
|
||||
|
||||
#define PATTY_KISS_TNC_BUFSZ 4096
|
||||
#define PATTY_KISS_TNC_PORT 0
|
||||
|
||||
typedef struct _patty_kiss_tnc patty_kiss_tnc;
|
||||
|
||||
#define PATTY_KISS_TNC_DEVICE (1 << 0)
|
||||
#define PATTY_KISS_TNC_FD (1 << 1)
|
||||
#define PATTY_KISS_TNC_BAUD (1 << 2)
|
||||
#define PATTY_KISS_TNC_FLOW (1 << 3)
|
||||
|
||||
enum patty_kiss_tnc_flow {
|
||||
PATTY_KISS_TNC_FLOW_NONE,
|
||||
PATTY_KISS_TNC_FLOW_CRTSCTS,
|
||||
PATTY_KISS_TNC_FLOW_XONXOFF
|
||||
};
|
||||
|
||||
typedef struct _patty_kiss_tnc_info {
|
||||
int flags;
|
||||
|
||||
const char *device;
|
||||
int fd;
|
||||
speed_t baud;
|
||||
enum patty_kiss_tnc_flow flow;
|
||||
} patty_kiss_tnc_info;
|
||||
|
||||
patty_ax25_if_driver *patty_kiss_tnc_driver();
|
||||
|
||||
patty_kiss_tnc *patty_kiss_tnc_new(patty_kiss_tnc_info *info);
|
||||
|
||||
int patty_kiss_tnc_fd(patty_kiss_tnc *tnc);
|
||||
|
||||
void patty_kiss_tnc_destroy(patty_kiss_tnc *tnc);
|
||||
|
||||
size_t patty_kiss_tnc_dropped(patty_kiss_tnc *tnc);
|
||||
|
||||
ssize_t patty_kiss_tnc_pending(patty_kiss_tnc *tnc);
|
||||
|
||||
ssize_t patty_kiss_tnc_recv(patty_kiss_tnc *tnc,
|
||||
void *buf,
|
||||
size_t len);
|
||||
|
||||
ssize_t patty_kiss_tnc_send(patty_kiss_tnc *tnc,
|
||||
const void *buf,
|
||||
size_t len);
|
||||
|
||||
#endif /* _PATTY_KISS_H */
|
106
src/if.c
106
src/if.c
|
@ -5,46 +5,36 @@
|
|||
#include <errno.h>
|
||||
|
||||
#include <patty/ax25.h>
|
||||
#include <patty/kiss.h>
|
||||
|
||||
static int init_tnc(patty_ax25_if *iface, patty_kiss_tnc_info *info) {
|
||||
if ((iface->tnc = patty_kiss_tnc_new(info)) == NULL) {
|
||||
goto error_kiss_tnc_new;
|
||||
}
|
||||
|
||||
iface->type = PATTY_AX25_IF_KISS_TNC;
|
||||
|
||||
return 0;
|
||||
|
||||
error_kiss_tnc_new:
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void destroy_tnc(patty_ax25_if *iface) {
|
||||
patty_kiss_tnc_destroy(iface->tnc);
|
||||
}
|
||||
|
||||
patty_ax25_if *patty_ax25_if_new(enum patty_ax25_if_type type,
|
||||
const char *name,
|
||||
patty_ax25_if_info *info,
|
||||
patty_ax25_addr *addr) {
|
||||
patty_ax25_if *patty_ax25_if_new(const char *name,
|
||||
patty_ax25_addr *addr,
|
||||
patty_ax25_if_driver *driver,
|
||||
patty_ax25_if_info *info) {
|
||||
patty_ax25_if *iface;
|
||||
|
||||
if ((iface = malloc(sizeof(*iface))) == NULL) {
|
||||
goto error_malloc_iface;
|
||||
}
|
||||
|
||||
memset(iface, '\0', sizeof(*iface));
|
||||
|
||||
strncpy(iface->name, name, sizeof(iface->name));
|
||||
|
||||
if (addr->callsign[0] == '\0') {
|
||||
errno = EINVAL;
|
||||
|
||||
goto error_invalid_address;
|
||||
}
|
||||
|
||||
if ((iface = malloc(sizeof(*iface))) == NULL) {
|
||||
goto error_malloc_iface;
|
||||
}
|
||||
|
||||
memset(iface, '\0', sizeof(*iface));
|
||||
|
||||
if ((iface->phy = driver->create(info)) == NULL) {
|
||||
goto error_phy_create;
|
||||
}
|
||||
|
||||
iface->driver = driver;
|
||||
|
||||
strncpy(iface->name, name, sizeof(iface->name));
|
||||
|
||||
/*
|
||||
* TODO: Eventually inherit the half/full-duplex flag from the TNC this
|
||||
* TODO: Eventually inherit the half/full-duplex flag from the PHY this
|
||||
* interface is bound to
|
||||
*/
|
||||
iface->flags_classes = PATTY_AX25_IF_DEFAULT_CLASSES;
|
||||
|
@ -69,28 +59,10 @@ patty_ax25_if *patty_ax25_if_new(enum patty_ax25_if_type type,
|
|||
goto error_dict_new_promisc_fds;
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case PATTY_AX25_IF_KISS_TNC:
|
||||
if (init_tnc(iface, info) < 0) {
|
||||
goto error_init;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
errno = ENOSYS;
|
||||
|
||||
goto error_invalid_if_type;
|
||||
}
|
||||
|
||||
memcpy(&iface->addr, addr, sizeof(iface->addr));
|
||||
|
||||
return iface;
|
||||
|
||||
error_invalid_if_type:
|
||||
error_init:
|
||||
patty_dict_destroy(iface->promisc_fds);
|
||||
|
||||
error_dict_new_promisc_fds:
|
||||
patty_list_destroy(iface->aliases);
|
||||
|
||||
|
@ -101,6 +73,9 @@ error_malloc_tx_buf:
|
|||
free(iface->rx_buf);
|
||||
|
||||
error_malloc_rx_buf:
|
||||
driver->destroy(iface->phy);
|
||||
|
||||
error_phy_create:
|
||||
free(iface);
|
||||
|
||||
error_malloc_iface:
|
||||
|
@ -109,13 +84,8 @@ error_invalid_address:
|
|||
}
|
||||
|
||||
void patty_ax25_if_destroy(patty_ax25_if *iface) {
|
||||
switch (iface->type) {
|
||||
case PATTY_AX25_IF_KISS_TNC:
|
||||
destroy_tnc(iface);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
if (iface->driver->destroy) {
|
||||
iface->driver->destroy(iface->phy);
|
||||
}
|
||||
|
||||
patty_dict_destroy(iface->promisc_fds);
|
||||
|
@ -126,6 +96,10 @@ void patty_ax25_if_destroy(patty_ax25_if *iface) {
|
|||
free(iface);
|
||||
}
|
||||
|
||||
char *patty_ax25_if_name(patty_ax25_if *iface) {
|
||||
return iface->name;
|
||||
}
|
||||
|
||||
int patty_ax25_if_addr_each(patty_ax25_if *iface,
|
||||
int (*callback)(patty_ax25_addr *, void *),
|
||||
void *ctx) {
|
||||
|
@ -297,21 +271,23 @@ static int handle_promisc_frame(uint32_t key,
|
|||
return patty_kiss_frame_send(fd, frame->buf, frame->len, 0);
|
||||
}
|
||||
|
||||
int patty_ax25_if_fd(patty_ax25_if *iface) {
|
||||
return iface->driver->fd(iface->phy);
|
||||
}
|
||||
|
||||
ssize_t patty_ax25_if_pending(patty_ax25_if *iface) {
|
||||
return patty_kiss_tnc_pending(iface->tnc);
|
||||
return iface->driver->pending(iface->phy);
|
||||
}
|
||||
|
||||
ssize_t patty_ax25_if_recv(patty_ax25_if *iface,
|
||||
void **buf) {
|
||||
ssize_t readlen;
|
||||
int port;
|
||||
struct promisc_frame frame;
|
||||
|
||||
if ((readlen = patty_kiss_tnc_recv(iface->tnc,
|
||||
if ((readlen = iface->driver->recv(iface->phy,
|
||||
iface->rx_buf,
|
||||
iface->mru,
|
||||
&port)) < 0) {
|
||||
goto error_kiss_tnc_recv;
|
||||
iface->mru)) < 0) {
|
||||
goto error_driver_recv;
|
||||
} else if (readlen == 0) {
|
||||
goto done;
|
||||
}
|
||||
|
@ -335,7 +311,7 @@ done:
|
|||
return readlen;
|
||||
|
||||
error_handle_promisc_frame:
|
||||
error_kiss_tnc_recv:
|
||||
error_driver_recv:
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
@ -345,8 +321,8 @@ ssize_t patty_ax25_if_send(patty_ax25_if *iface,
|
|||
ssize_t wrlen;
|
||||
struct promisc_frame frame;
|
||||
|
||||
if ((wrlen = patty_kiss_tnc_send(iface->tnc, buf, len, 0)) < 0) {
|
||||
goto error_kiss_tnc_send;
|
||||
if ((wrlen = iface->driver->send(iface->phy, buf, len)) < 0) {
|
||||
goto error_driver_send;
|
||||
}
|
||||
|
||||
iface->stats.tx_frames++;
|
||||
|
@ -365,6 +341,6 @@ ssize_t patty_ax25_if_send(patty_ax25_if *iface,
|
|||
return wrlen;
|
||||
|
||||
error_handle_promisc_frame:
|
||||
error_kiss_tnc_send:
|
||||
error_driver_send:
|
||||
return -1;
|
||||
}
|
||||
|
|
325
src/kiss.c
325
src/kiss.c
|
@ -15,35 +15,6 @@
|
|||
|
||||
#include "config.h"
|
||||
|
||||
enum kiss_flags {
|
||||
KISS_NONE = 0,
|
||||
KISS_FRAME = 1 << 0,
|
||||
KISS_COMMAND = 1 << 1,
|
||||
KISS_ESCAPE = 1 << 2
|
||||
};
|
||||
|
||||
enum tnc_opts {
|
||||
TNC_NONE = 0,
|
||||
TNC_CLOSE_ON_DESTROY = 1 << 0
|
||||
};
|
||||
|
||||
struct _patty_kiss_tnc {
|
||||
struct termios attrs,
|
||||
attrs_old;
|
||||
|
||||
int fd,
|
||||
opts;
|
||||
|
||||
void *buf,
|
||||
*frame;
|
||||
|
||||
size_t bufsz,
|
||||
offset,
|
||||
dropped;
|
||||
|
||||
ssize_t readlen;
|
||||
};
|
||||
|
||||
static inline ssize_t write_byte(int fd, uint8_t c) {
|
||||
return write(fd, &c, sizeof(c));
|
||||
}
|
||||
|
@ -121,299 +92,3 @@ ssize_t patty_kiss_frame_send(int fd,
|
|||
error_io:
|
||||
return -1;
|
||||
}
|
||||
|
||||
patty_kiss_tnc *patty_kiss_tnc_new(patty_kiss_tnc_info *info) {
|
||||
patty_kiss_tnc *tnc;
|
||||
|
||||
if ((tnc = malloc(sizeof(*tnc))) == NULL) {
|
||||
goto error_malloc_tnc;
|
||||
}
|
||||
|
||||
if ((tnc->buf = malloc(PATTY_KISS_BUFSZ)) == NULL) {
|
||||
goto error_malloc_buf;
|
||||
}
|
||||
|
||||
if (info->flags & PATTY_KISS_TNC_DEVICE) {
|
||||
struct stat st;
|
||||
|
||||
if (strcmp(info->device, "/dev/ptmx") == 0) {
|
||||
int ptysub;
|
||||
|
||||
if (openpty(&tnc->fd, &ptysub, NULL, NULL, NULL) < 0) {
|
||||
goto error_open;
|
||||
}
|
||||
} else if (stat(info->device, &st) < 0) {
|
||||
goto error_stat;
|
||||
} else if ((st.st_mode & S_IFMT) == S_IFSOCK) {
|
||||
struct sockaddr_un addr;
|
||||
|
||||
if (strlen(info->device) > sizeof(addr.sun_path)) {
|
||||
errno = EOVERFLOW;
|
||||
|
||||
goto error_overflow;
|
||||
}
|
||||
|
||||
if ((tnc->fd = socket(PF_UNIX, SOCK_STREAM, 0)) < 0) {
|
||||
goto error_socket;
|
||||
}
|
||||
|
||||
memset(&addr, '\0', sizeof(addr));
|
||||
addr.sun_family = AF_UNIX;
|
||||
strncpy(addr.sun_path, info->device, sizeof(addr.sun_path)-1);
|
||||
|
||||
if (connect(tnc->fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
|
||||
goto error_connect;
|
||||
}
|
||||
} else {
|
||||
if ((tnc->fd = open(info->device, O_RDWR | O_NOCTTY)) < 0) {
|
||||
goto error_open;
|
||||
}
|
||||
}
|
||||
|
||||
tnc->opts |= TNC_CLOSE_ON_DESTROY;
|
||||
} else if (info->flags & PATTY_KISS_TNC_FD) {
|
||||
tnc->fd = info->fd;
|
||||
} else {
|
||||
errno = EINVAL;
|
||||
|
||||
goto error_no_fd;
|
||||
}
|
||||
|
||||
if (isatty(tnc->fd) && ptsname(tnc->fd) == NULL) {
|
||||
if (tcgetattr(tnc->fd, &tnc->attrs) < 0) {
|
||||
goto error_tcgetattr;
|
||||
}
|
||||
|
||||
memcpy(&tnc->attrs_old, &tnc->attrs, sizeof(tnc->attrs_old));
|
||||
|
||||
cfmakeraw(&tnc->attrs);
|
||||
|
||||
if (info->flags & PATTY_KISS_TNC_BAUD) {
|
||||
cfsetspeed(&tnc->attrs, info->baud);
|
||||
}
|
||||
|
||||
if (info->flags & PATTY_KISS_TNC_FLOW) {
|
||||
switch (info->flow) {
|
||||
case PATTY_KISS_TNC_FLOW_NONE:
|
||||
break;
|
||||
|
||||
case PATTY_KISS_TNC_FLOW_CRTSCTS:
|
||||
tnc->attrs.c_cflag |= CRTSCTS;
|
||||
|
||||
break;
|
||||
|
||||
case PATTY_KISS_TNC_FLOW_XONXOFF:
|
||||
tnc->attrs.c_iflag |= IXON | IXOFF;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (tcflush(tnc->fd, TCIOFLUSH) < 0) {
|
||||
goto error_tcflush;
|
||||
}
|
||||
|
||||
if (tcsetattr(tnc->fd, TCSANOW, &tnc->attrs) < 0) {
|
||||
goto error_tcsetattr;
|
||||
}
|
||||
} else {
|
||||
errno = 0;
|
||||
}
|
||||
|
||||
tnc->opts = TNC_NONE;
|
||||
tnc->bufsz = PATTY_KISS_BUFSZ;
|
||||
tnc->offset = 0;
|
||||
tnc->dropped = 0;
|
||||
tnc->readlen = -1;
|
||||
|
||||
return tnc;
|
||||
|
||||
error_tcflush:
|
||||
error_tcsetattr:
|
||||
error_tcgetattr:
|
||||
error_connect:
|
||||
if (info->flags & PATTY_KISS_TNC_DEVICE) {
|
||||
(void)close(tnc->fd);
|
||||
}
|
||||
|
||||
error_open:
|
||||
error_no_fd:
|
||||
error_socket:
|
||||
error_overflow:
|
||||
error_stat:
|
||||
free(tnc->buf);
|
||||
|
||||
error_malloc_buf:
|
||||
free(tnc);
|
||||
|
||||
error_malloc_tnc:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int patty_kiss_tnc_fd(patty_kiss_tnc *tnc) {
|
||||
return tnc->fd;
|
||||
}
|
||||
|
||||
void patty_kiss_tnc_destroy(patty_kiss_tnc *tnc) {
|
||||
if (isatty(tnc->fd) && ptsname(tnc->fd) == NULL) {
|
||||
(void)tcsetattr(tnc->fd, TCSANOW, &tnc->attrs_old);
|
||||
}
|
||||
|
||||
if (tnc->opts & TNC_CLOSE_ON_DESTROY) {
|
||||
(void)close(tnc->fd);
|
||||
}
|
||||
|
||||
free(tnc->buf);
|
||||
free(tnc);
|
||||
}
|
||||
|
||||
static void tnc_drop(patty_kiss_tnc *tnc) {
|
||||
tnc->offset = 0;
|
||||
tnc->readlen = -1;
|
||||
tnc->dropped++;
|
||||
}
|
||||
|
||||
size_t patty_kiss_tnc_dropped(patty_kiss_tnc *tnc) {
|
||||
return tnc->dropped;
|
||||
}
|
||||
|
||||
ssize_t patty_kiss_tnc_pending(patty_kiss_tnc *tnc) {
|
||||
if (tnc->readlen < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (tnc->offset == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return tnc->readlen - tnc->offset;
|
||||
}
|
||||
|
||||
ssize_t patty_kiss_tnc_recv(patty_kiss_tnc *tnc,
|
||||
void *buf,
|
||||
size_t len,
|
||||
int *port) {
|
||||
size_t r = 0, /* Number of bytes read */
|
||||
w = 0; /* Number of bytes written to buf */
|
||||
|
||||
int flags = KISS_NONE;
|
||||
|
||||
if (tnc->offset == tnc->readlen) {
|
||||
errno = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
while (1) {
|
||||
uint8_t c;
|
||||
|
||||
r++;
|
||||
|
||||
if (w == len) {
|
||||
tnc_drop(tnc);
|
||||
|
||||
flags &= ~KISS_FRAME;
|
||||
w = 0;
|
||||
}
|
||||
|
||||
if (tnc->offset == 0) {
|
||||
if ((tnc->readlen = read(tnc->fd, tnc->buf, tnc->bufsz)) < 0) {
|
||||
goto error_io;
|
||||
} else if (tnc->readlen == 0) {
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
c = ((uint8_t *)tnc->buf)[tnc->offset++];
|
||||
|
||||
if (tnc->offset == tnc->readlen) {
|
||||
tnc->offset = 0;
|
||||
}
|
||||
|
||||
if (!(flags & KISS_FRAME)) {
|
||||
if (c == PATTY_KISS_FEND) {
|
||||
flags = KISS_FRAME;
|
||||
|
||||
continue;
|
||||
} else {
|
||||
errno = EIO;
|
||||
|
||||
goto error_io;
|
||||
}
|
||||
} else {
|
||||
if (c == PATTY_KISS_FEND) {
|
||||
flags = KISS_NONE;
|
||||
|
||||
if (w > 0) {
|
||||
goto done;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (!(flags & KISS_COMMAND)) {
|
||||
if (PATTY_KISS_COMMAND(c) != PATTY_KISS_DATA) {
|
||||
errno = EIO;
|
||||
|
||||
goto error_io;
|
||||
}
|
||||
|
||||
if (port) {
|
||||
*port = PATTY_KISS_COMMAND_PORT(c);
|
||||
}
|
||||
|
||||
flags |= KISS_COMMAND;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!(flags & KISS_ESCAPE)) {
|
||||
if (c == PATTY_KISS_FESC) {
|
||||
flags |= KISS_ESCAPE;
|
||||
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
switch (c) {
|
||||
case PATTY_KISS_TFEND:
|
||||
((uint8_t *)buf)[w++] = PATTY_KISS_FEND;
|
||||
flags &= ~KISS_ESCAPE;
|
||||
continue;
|
||||
|
||||
case PATTY_KISS_TFESC:
|
||||
((uint8_t *)buf)[w++] = PATTY_KISS_FESC;
|
||||
flags &= ~KISS_ESCAPE;
|
||||
continue;
|
||||
|
||||
default:
|
||||
errno = EIO;
|
||||
|
||||
goto error_io;
|
||||
}
|
||||
}
|
||||
|
||||
((uint8_t *)buf)[w++] = c;
|
||||
}
|
||||
|
||||
done:
|
||||
if (flags & KISS_FRAME) {
|
||||
tnc_drop(tnc);
|
||||
|
||||
errno = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
return (ssize_t)w;
|
||||
|
||||
error_io:
|
||||
return -1;
|
||||
}
|
||||
|
||||
ssize_t patty_kiss_tnc_send(patty_kiss_tnc *tnc,
|
||||
const void *buf,
|
||||
size_t len,
|
||||
int port) {
|
||||
return patty_kiss_frame_send(tnc->fd, buf, len, port);
|
||||
}
|
||||
|
|
|
@ -432,7 +432,7 @@ int patty_ax25_server_if_add(patty_ax25_server *server,
|
|||
patty_ax25_if *iface) {
|
||||
int fd;
|
||||
|
||||
if ((fd = patty_kiss_tnc_fd(iface->tnc)) >= 0) {
|
||||
if ((fd = patty_ax25_if_fd(iface)) >= 0) {
|
||||
fd_watch(server, fd);
|
||||
}
|
||||
|
||||
|
@ -456,7 +456,7 @@ int patty_ax25_server_if_delete(patty_ax25_server *server,
|
|||
patty_ax25_if *iface = item->value;
|
||||
|
||||
if (strncmp(iface->name, ifname, sizeof(iface->name)) == 0) {
|
||||
if ((fd = patty_kiss_tnc_fd(iface->tnc)) >= 0) {
|
||||
if ((fd = patty_ax25_if_fd(iface)) >= 0) {
|
||||
fd_clear(server, fd);
|
||||
}
|
||||
|
||||
|
@ -2006,7 +2006,7 @@ error_decode:
|
|||
}
|
||||
|
||||
static int handle_iface(patty_ax25_server *server, patty_ax25_if *iface) {
|
||||
int fd = patty_kiss_tnc_fd(iface->tnc);
|
||||
int fd = patty_ax25_if_fd(iface);
|
||||
|
||||
if (!FD_ISSET(fd, &server->fds_r)) {
|
||||
goto done;
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#include <errno.h>
|
||||
|
||||
#include <patty/ax25.h>
|
||||
#include <patty/kiss/tnc.h>
|
||||
|
||||
#include "config.h"
|
||||
|
||||
|
@ -621,15 +622,13 @@ error_invalid_type:
|
|||
ssize_t patty_ax25_sock_recv(patty_ax25_sock *sock,
|
||||
void *buf,
|
||||
size_t len) {
|
||||
int port;
|
||||
|
||||
if (sock->type != PATTY_AX25_SOCK_RAW) {
|
||||
errno = EINVAL;
|
||||
|
||||
goto error_invalid_type;
|
||||
}
|
||||
|
||||
return patty_kiss_tnc_recv(sock->raw, buf, len, &port);
|
||||
return patty_kiss_tnc_recv(sock->raw, buf, len);
|
||||
|
||||
error_invalid_type:
|
||||
return -1;
|
||||
|
|
348
src/tnc.c
Normal file
348
src/tnc.c
Normal file
|
@ -0,0 +1,348 @@
|
|||
#define _GNU_SOURCE
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <inttypes.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <termios.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <patty/kiss.h>
|
||||
#include <patty/kiss/tnc.h>
|
||||
|
||||
#include "config.h"
|
||||
|
||||
enum state_flags {
|
||||
STATE_NONE = 0,
|
||||
STATE_FRAME = 1 << 0,
|
||||
STATE_COMMAND = 1 << 1,
|
||||
STATE_ESCAPE = 1 << 2
|
||||
};
|
||||
|
||||
enum tnc_opts {
|
||||
TNC_NONE = 0,
|
||||
TNC_CLOSE_ON_DESTROY = 1 << 0
|
||||
};
|
||||
|
||||
struct _patty_kiss_tnc {
|
||||
struct termios attrs,
|
||||
attrs_old;
|
||||
|
||||
int fd,
|
||||
opts;
|
||||
|
||||
void *buf,
|
||||
*frame;
|
||||
|
||||
size_t bufsz,
|
||||
offset,
|
||||
dropped;
|
||||
|
||||
ssize_t readlen;
|
||||
};
|
||||
|
||||
patty_kiss_tnc *patty_kiss_tnc_new(patty_kiss_tnc_info *info) {
|
||||
patty_kiss_tnc *tnc;
|
||||
|
||||
if ((tnc = malloc(sizeof(*tnc))) == NULL) {
|
||||
goto error_malloc_tnc;
|
||||
}
|
||||
|
||||
if ((tnc->buf = malloc(PATTY_KISS_TNC_BUFSZ)) == NULL) {
|
||||
goto error_malloc_buf;
|
||||
}
|
||||
|
||||
if (info->flags & PATTY_KISS_TNC_DEVICE) {
|
||||
struct stat st;
|
||||
|
||||
if (strcmp(info->device, "/dev/ptmx") == 0) {
|
||||
int ptysub;
|
||||
|
||||
if (openpty(&tnc->fd, &ptysub, NULL, NULL, NULL) < 0) {
|
||||
goto error_open;
|
||||
}
|
||||
} else if (stat(info->device, &st) < 0) {
|
||||
goto error_stat;
|
||||
} else if ((st.st_mode & S_IFMT) == S_IFSOCK) {
|
||||
struct sockaddr_un addr;
|
||||
|
||||
if (strlen(info->device) > sizeof(addr.sun_path)) {
|
||||
errno = EOVERFLOW;
|
||||
|
||||
goto error_overflow;
|
||||
}
|
||||
|
||||
if ((tnc->fd = socket(PF_UNIX, SOCK_STREAM, 0)) < 0) {
|
||||
goto error_socket;
|
||||
}
|
||||
|
||||
memset(&addr, '\0', sizeof(addr));
|
||||
addr.sun_family = AF_UNIX;
|
||||
strncpy(addr.sun_path, info->device, sizeof(addr.sun_path)-1);
|
||||
|
||||
if (connect(tnc->fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
|
||||
goto error_connect;
|
||||
}
|
||||
} else {
|
||||
if ((tnc->fd = open(info->device, O_RDWR | O_NOCTTY)) < 0) {
|
||||
goto error_open;
|
||||
}
|
||||
}
|
||||
|
||||
tnc->opts |= TNC_CLOSE_ON_DESTROY;
|
||||
} else if (info->flags & PATTY_KISS_TNC_FD) {
|
||||
tnc->fd = info->fd;
|
||||
} else {
|
||||
errno = EINVAL;
|
||||
|
||||
goto error_no_fd;
|
||||
}
|
||||
|
||||
if (isatty(tnc->fd) && ptsname(tnc->fd) == NULL) {
|
||||
if (tcgetattr(tnc->fd, &tnc->attrs) < 0) {
|
||||
goto error_tcgetattr;
|
||||
}
|
||||
|
||||
memcpy(&tnc->attrs_old, &tnc->attrs, sizeof(tnc->attrs_old));
|
||||
|
||||
cfmakeraw(&tnc->attrs);
|
||||
|
||||
if (info->flags & PATTY_KISS_TNC_BAUD) {
|
||||
cfsetspeed(&tnc->attrs, info->baud);
|
||||
}
|
||||
|
||||
if (info->flags & PATTY_KISS_TNC_FLOW) {
|
||||
switch (info->flow) {
|
||||
case PATTY_KISS_TNC_FLOW_NONE:
|
||||
break;
|
||||
|
||||
case PATTY_KISS_TNC_FLOW_CRTSCTS:
|
||||
tnc->attrs.c_cflag |= CRTSCTS;
|
||||
|
||||
break;
|
||||
|
||||
case PATTY_KISS_TNC_FLOW_XONXOFF:
|
||||
tnc->attrs.c_iflag |= IXON | IXOFF;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (tcflush(tnc->fd, TCIOFLUSH) < 0) {
|
||||
goto error_tcflush;
|
||||
}
|
||||
|
||||
if (tcsetattr(tnc->fd, TCSANOW, &tnc->attrs) < 0) {
|
||||
goto error_tcsetattr;
|
||||
}
|
||||
} else {
|
||||
errno = 0;
|
||||
}
|
||||
|
||||
tnc->opts = TNC_NONE;
|
||||
tnc->bufsz = PATTY_KISS_TNC_BUFSZ;
|
||||
tnc->offset = 0;
|
||||
tnc->dropped = 0;
|
||||
tnc->readlen = -1;
|
||||
|
||||
return tnc;
|
||||
|
||||
error_tcflush:
|
||||
error_tcsetattr:
|
||||
error_tcgetattr:
|
||||
error_connect:
|
||||
if (info->flags & PATTY_KISS_TNC_DEVICE) {
|
||||
(void)close(tnc->fd);
|
||||
}
|
||||
|
||||
error_open:
|
||||
error_no_fd:
|
||||
error_socket:
|
||||
error_overflow:
|
||||
error_stat:
|
||||
free(tnc->buf);
|
||||
|
||||
error_malloc_buf:
|
||||
free(tnc);
|
||||
|
||||
error_malloc_tnc:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void patty_kiss_tnc_destroy(patty_kiss_tnc *tnc) {
|
||||
if (isatty(tnc->fd) && ptsname(tnc->fd) == NULL) {
|
||||
(void)tcsetattr(tnc->fd, TCSANOW, &tnc->attrs_old);
|
||||
}
|
||||
|
||||
if (tnc->opts & TNC_CLOSE_ON_DESTROY) {
|
||||
(void)close(tnc->fd);
|
||||
}
|
||||
|
||||
free(tnc->buf);
|
||||
free(tnc);
|
||||
}
|
||||
|
||||
int patty_kiss_tnc_fd(patty_kiss_tnc *tnc) {
|
||||
return tnc->fd;
|
||||
}
|
||||
|
||||
static void tnc_drop(patty_kiss_tnc *tnc) {
|
||||
tnc->offset = 0;
|
||||
tnc->readlen = -1;
|
||||
tnc->dropped++;
|
||||
}
|
||||
|
||||
size_t patty_kiss_tnc_dropped(patty_kiss_tnc *tnc) {
|
||||
return tnc->dropped;
|
||||
}
|
||||
|
||||
ssize_t patty_kiss_tnc_pending(patty_kiss_tnc *tnc) {
|
||||
if (tnc->readlen < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (tnc->offset == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return tnc->readlen - tnc->offset;
|
||||
}
|
||||
|
||||
ssize_t patty_kiss_tnc_recv(patty_kiss_tnc *tnc,
|
||||
void *buf,
|
||||
size_t len) {
|
||||
size_t r = 0, /* Number of bytes read */
|
||||
w = 0; /* Number of bytes written to buf */
|
||||
|
||||
int flags = STATE_NONE;
|
||||
|
||||
if (tnc->offset == tnc->readlen) {
|
||||
errno = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
while (1) {
|
||||
uint8_t c;
|
||||
|
||||
r++;
|
||||
|
||||
if (w == len) {
|
||||
tnc_drop(tnc);
|
||||
|
||||
flags &= ~STATE_FRAME;
|
||||
w = 0;
|
||||
}
|
||||
|
||||
if (tnc->offset == 0) {
|
||||
if ((tnc->readlen = read(tnc->fd, tnc->buf, tnc->bufsz)) < 0) {
|
||||
goto error_io;
|
||||
} else if (tnc->readlen == 0) {
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
c = ((uint8_t *)tnc->buf)[tnc->offset++];
|
||||
|
||||
if (tnc->offset == tnc->readlen) {
|
||||
tnc->offset = 0;
|
||||
}
|
||||
|
||||
if (!(flags & STATE_FRAME)) {
|
||||
if (c == PATTY_KISS_FEND) {
|
||||
flags = STATE_FRAME;
|
||||
|
||||
continue;
|
||||
} else {
|
||||
errno = EIO;
|
||||
|
||||
goto error_io;
|
||||
}
|
||||
} else {
|
||||
if (c == PATTY_KISS_FEND) {
|
||||
flags = STATE_NONE;
|
||||
|
||||
if (w > 0) {
|
||||
goto done;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (!(flags & STATE_COMMAND)) {
|
||||
if (PATTY_KISS_COMMAND(c) != PATTY_KISS_DATA) {
|
||||
errno = EIO;
|
||||
|
||||
goto error_io;
|
||||
}
|
||||
|
||||
flags |= STATE_COMMAND;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!(flags & STATE_ESCAPE)) {
|
||||
if (c == PATTY_KISS_FESC) {
|
||||
flags |= STATE_ESCAPE;
|
||||
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
switch (c) {
|
||||
case PATTY_KISS_TFEND:
|
||||
((uint8_t *)buf)[w++] = PATTY_KISS_FEND;
|
||||
flags &= ~STATE_ESCAPE;
|
||||
continue;
|
||||
|
||||
case PATTY_KISS_TFESC:
|
||||
((uint8_t *)buf)[w++] = PATTY_KISS_FESC;
|
||||
flags &= ~STATE_ESCAPE;
|
||||
continue;
|
||||
|
||||
default:
|
||||
errno = EIO;
|
||||
|
||||
goto error_io;
|
||||
}
|
||||
}
|
||||
|
||||
((uint8_t *)buf)[w++] = c;
|
||||
}
|
||||
|
||||
done:
|
||||
if (flags & STATE_FRAME) {
|
||||
tnc_drop(tnc);
|
||||
|
||||
errno = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
return (ssize_t)w;
|
||||
|
||||
error_io:
|
||||
return -1;
|
||||
}
|
||||
|
||||
ssize_t patty_kiss_tnc_send(patty_kiss_tnc *tnc,
|
||||
const void *buf,
|
||||
size_t len) {
|
||||
return patty_kiss_frame_send(tnc->fd, buf, len, PATTY_KISS_TNC_PORT);
|
||||
}
|
||||
|
||||
patty_ax25_if_driver *patty_kiss_tnc_driver() {
|
||||
static patty_ax25_if_driver driver = {
|
||||
.destroy = (patty_ax25_if_driver_destroy *)patty_kiss_tnc_destroy,
|
||||
.fd = (patty_ax25_if_driver_fd *)patty_kiss_tnc_fd,
|
||||
.pending = (patty_ax25_if_driver_pending *)patty_kiss_tnc_pending,
|
||||
.recv = (patty_ax25_if_driver_recv *)patty_kiss_tnc_recv,
|
||||
.send = (patty_ax25_if_driver_send *)patty_kiss_tnc_send
|
||||
};
|
||||
|
||||
return &driver;
|
||||
}
|
Loading…
Add table
Reference in a new issue