From cb33d799ffcd4c4528747d63f83003063ad06ae1 Mon Sep 17 00:00:00 2001 From: XANTRONIX Development Date: Sun, 13 Sep 2020 16:56:10 -0400 Subject: [PATCH] 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 --- bin/ax25dump.c | 31 ++-- bin/if.c | 195 +++++++++++++-------- bin/kiss.c | 11 +- bin/pattyd.c | 45 +++-- include/patty/ax25.h | 2 +- include/patty/ax25/if.h | 52 ++++-- include/patty/ax25/sock.h | 2 +- include/patty/bin/if.h | 14 +- include/patty/bin/kiss.h | 12 +- include/patty/kiss.h | 47 +---- include/patty/kiss/tnc.h | 54 ++++++ src/if.c | 106 +++++------- src/kiss.c | 325 ----------------------------------- src/server.c | 6 +- src/sock.c | 5 +- src/tnc.c | 348 ++++++++++++++++++++++++++++++++++++++ 16 files changed, 669 insertions(+), 586 deletions(-) create mode 100644 include/patty/kiss/tnc.h create mode 100644 src/tnc.c diff --git a/bin/ax25dump.c b/bin/ax25dump.c index e31754f..39ea88f 100644 --- a/bin/ax25dump.c +++ b/bin/ax25dump.c @@ -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; diff --git a/bin/if.c b/bin/if.c index 7f35657..fa2eaaa 100644 --- a/bin/if.c +++ b/bin/if.c @@ -6,6 +6,13 @@ #include +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; ierr, "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; iname = 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; } diff --git a/bin/kiss.c b/bin/kiss.c index eb23a84..e4299ee 100644 --- a/bin/kiss.c +++ b/bin/kiss.c @@ -4,10 +4,13 @@ #include -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; } diff --git a/bin/pattyd.c b/bin/pattyd.c index bc539ce..9ae291d 100644 --- a/bin/pattyd.c +++ b/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) { diff --git a/include/patty/ax25.h b/include/patty/ax25.h index a52c8e9..a67a304 100644 --- a/include/patty/ax25.h +++ b/include/patty/ax25.h @@ -5,7 +5,6 @@ #include #include -#include #include #include @@ -104,6 +103,7 @@ typedef struct _patty_ax25_if patty_ax25_if; #include #include #include +#include #include #include #include diff --git a/include/patty/ax25/if.h b/include/patty/ax25/if.h index 14e1095..5c82a20 100644 --- a/include/patty/ax25/if.h +++ b/include/patty/ax25/if.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, diff --git a/include/patty/ax25/sock.h b/include/patty/ax25/sock.h index 355d91a..fd061b5 100644 --- a/include/patty/ax25/sock.h +++ b/include/patty/ax25/sock.h @@ -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 diff --git a/include/patty/bin/if.h b/include/patty/bin/if.h index 6a145bb..36fb584 100644 --- a/include/patty/bin/if.h +++ b/include/patty/bin/if.h @@ -2,16 +2,10 @@ #define _PATTY_BIN_IF_H #include +#include -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 */ diff --git a/include/patty/bin/kiss.h b/include/patty/bin/kiss.h index 5502bd6..360cf80 100644 --- a/include/patty/bin/kiss.h +++ b/include/patty/bin/kiss.h @@ -1,13 +1,13 @@ #ifndef _PATTY_BIN_KISS_H #define _PATTY_BIN_KISS_H -#include +#include -typedef struct _patty_bin_kiss_data { - char err[256]; - patty_kiss_tnc_info info; -} patty_bin_kiss_data; +#include -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 */ diff --git a/include/patty/kiss.h b/include/patty/kiss.h index b663dae..bde7254 100644 --- a/include/patty/kiss.h +++ b/include/patty/kiss.h @@ -2,17 +2,14 @@ #define _PATTY_KISS_H #include -#include #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 */ diff --git a/include/patty/kiss/tnc.h b/include/patty/kiss/tnc.h new file mode 100644 index 0000000..9ffcf45 --- /dev/null +++ b/include/patty/kiss/tnc.h @@ -0,0 +1,54 @@ +#ifndef _PATTY_KISS_TNC_H +#define _PATTY_KISS_TNC_H + +#include +#include + +#include + +#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 */ diff --git a/src/if.c b/src/if.c index 988fa05..ff36df0 100644 --- a/src/if.c +++ b/src/if.c @@ -5,46 +5,36 @@ #include #include +#include -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; } diff --git a/src/kiss.c b/src/kiss.c index 1b4bf4e..bea4d27 100644 --- a/src/kiss.c +++ b/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); -} diff --git a/src/server.c b/src/server.c index f832e9e..3ddd9a5 100644 --- a/src/server.c +++ b/src/server.c @@ -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; diff --git a/src/sock.c b/src/sock.c index d9f6236..c9eb926 100644 --- a/src/sock.c +++ b/src/sock.c @@ -10,6 +10,7 @@ #include #include +#include #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; diff --git a/src/tnc.c b/src/tnc.c new file mode 100644 index 0000000..5175c98 --- /dev/null +++ b/src/tnc.c @@ -0,0 +1,348 @@ +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#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; +}