patty/src/kiss.c

294 lines
5.8 KiB
C
Raw Normal View History

#include <stdlib.h>
2015-07-16 00:31:27 +00:00
#include <string.h>
#include <inttypes.h>
#include <fcntl.h>
#include <unistd.h>
2015-07-14 16:45:03 +00:00
#include <sys/types.h>
#include <sys/stat.h>
2015-07-14 16:45:03 +00:00
#include <sys/uio.h>
#include <errno.h>
#include <patty/kiss.h>
enum kiss_flags {
KISS_NONE = 0,
KISS_FRAME = 1 << 0,
KISS_COMMAND = 1 << 1,
KISS_ESCAPE = 1 << 2
};
struct _patty_kiss_tnc {
2020-05-26 23:52:06 -04:00
int fd;
void *buf,
*frame;
2020-05-26 23:52:06 -04:00
size_t bufsz,
buflen,
dropped;
};
patty_kiss_tnc *patty_kiss_tnc_open_fd(int fd) {
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;
}
tnc->fd = fd;
tnc->bufsz = PATTY_KISS_BUFSZ;
2015-07-15 21:59:56 -05:00
tnc->buflen = 0;
2020-05-26 23:52:06 -04:00
tnc->dropped = 0;
return tnc;
error_malloc_buf:
free(tnc);
error_malloc_tnc:
return NULL;
}
patty_kiss_tnc *patty_kiss_tnc_open(const char *device) {
patty_kiss_tnc *tnc;
int fd;
if ((fd = open(device, O_RDWR)) < 0) {
goto error_open;
}
if ((tnc = patty_kiss_tnc_open_fd(fd)) == NULL) {
goto error_tnc_open_fd;
}
return tnc;
error_tnc_open_fd:
close(fd);
error_open:
return NULL;
}
2015-07-29 22:28:44 -05:00
int patty_kiss_tnc_fd_unix(patty_kiss_tnc *tnc) {
return tnc->fd;
}
void patty_kiss_tnc_close(patty_kiss_tnc *tnc) {
close(tnc->fd);
2015-07-15 20:29:50 -05:00
free(tnc->buf);
free(tnc);
}
static ssize_t tnc_buffer(patty_kiss_tnc *tnc) {
ssize_t readlen;
if (tnc->buflen == tnc->bufsz) {
goto full;
}
2015-07-16 00:31:27 +00:00
if ((readlen = read(tnc->fd, tnc->buf, tnc->bufsz - tnc->buflen)) < 0) {
goto error_read;
2015-07-12 12:00:35 -05:00
}
tnc->buflen += readlen;
return readlen;
2015-07-15 20:32:24 -05:00
full:
return 0;
error_read:
2015-07-15 20:32:24 -05:00
return -1;
}
static void tnc_flush(patty_kiss_tnc *tnc, size_t len) {
memmove(tnc->buf, ((uint8_t *)tnc->buf) + len, tnc->buflen - len);
2015-07-15 20:32:24 -05:00
tnc->buflen -= len;
}
static void tnc_drop(patty_kiss_tnc *tnc) {
tnc->buflen = 0;
tnc->dropped++;
}
size_t patty_kiss_tnc_dropped(patty_kiss_tnc *tnc) {
return tnc->dropped;
}
2020-05-27 00:22:50 -04:00
ssize_t patty_kiss_tnc_recv(patty_kiss_tnc *tnc,
void *buf,
size_t len,
int *port) {
size_t i = 0,
b = 0;
2015-07-16 00:31:27 +00:00
enum kiss_flags flags = KISS_NONE;
if (tnc_buffer(tnc) < 0) {
goto error_io;
}
while (i < tnc->buflen && b < len) {
uint8_t c = ((uint8_t *)tnc->buf)[i++];
if (!(flags & KISS_FRAME)) {
if (c == PATTY_KISS_FEND) {
flags |= KISS_FRAME;
2015-07-16 00:31:27 +00:00
continue;
} else {
errno = EIO;
goto error_io;
}
} else {
if (c == PATTY_KISS_FEND) {
if (b > 0) {
flags &= ~KISS_FRAME;
goto done;
}
2015-07-16 00:31:27 +00:00
continue;
}
}
if (!(flags & KISS_COMMAND)) {
if (PATTY_KISS_COMMAND(c) != PATTY_KISS_DATA) {
2015-07-12 12:00:35 -05:00
errno = EIO;
goto error_io;
}
*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)[b++] = PATTY_KISS_FEND;
flags &= ~KISS_ESCAPE;
continue;
case PATTY_KISS_TFESC:
((uint8_t *)buf)[b++] = PATTY_KISS_FESC;
flags &= ~KISS_ESCAPE;
continue;
default:
errno = EIO;
goto error_io;
}
2015-07-16 00:31:27 +00:00
}
((uint8_t *)buf)[b++] = c;
}
done:
if (flags & KISS_FRAME) {
tnc_drop(tnc);
} else {
tnc_flush(tnc, i);
}
2015-07-16 00:31:27 +00:00
return (ssize_t)b;
error_io:
return -1;
}
static inline ssize_t write_byte(int fd, uint8_t c) {
return write(fd, &c, sizeof(c));
}
static inline ssize_t write_start(int fd,
enum patty_kiss_command command,
int port) {
uint8_t start[2] = {
PATTY_KISS_FEND,
((port & 0x0f) << 4) | (command & 0x0f)
};
return write(fd, start, sizeof(start));
}
static uint8_t escape_fend[2] = { PATTY_KISS_FESC, PATTY_KISS_TFEND };
static uint8_t escape_fesc[2] = { PATTY_KISS_FESC, PATTY_KISS_TFESC };
ssize_t patty_kiss_tnc_send(patty_kiss_tnc *tnc,
const void *buf,
size_t len,
int port) {
size_t i, start = 0, end = 0;
if (write_start(tnc->fd, PATTY_KISS_DATA, port) < 0) {
goto error_io;
}
for (i=0; i<len; i++) {
uint8_t c = ((uint8_t *)buf)[i];
uint8_t *escape = NULL;
switch (c) {
case PATTY_KISS_FEND:
escape = escape_fend;
break;
case PATTY_KISS_FESC:
escape = escape_fesc;
break;
2015-07-14 16:45:03 +00:00
default:
end = i + 1;
2015-07-14 16:45:03 +00:00
break;
}
if (escape) {
if (write(tnc->fd, ((uint8_t *)buf) + start, end - start) < 0) {
goto error_io;
}
2015-07-16 00:31:27 +00:00
if (write(tnc->fd, escape, 2) < 0) {
goto error_io;
}
escape = NULL;
start = i + 1;
end = start;
}
}
if (end - start) {
if (write(tnc->fd, ((uint8_t *)buf) + start, end - start) < 0) {
goto error_io;
}
}
2015-07-16 00:31:27 +00:00
if (write_byte(tnc->fd, PATTY_KISS_FEND) < 0) {
goto error_io;
}
return len;
error_io:
return -1;
}