#include #include #include #include #include #include #include #include #include #include enum kiss_flags { KISS_NONE = 0, KISS_FRAME = 1 << 0, KISS_COMMAND = 1 << 1, KISS_ESCAPE = 1 << 2 }; struct _patty_kiss_tnc { int fd; void *buf, *frame; size_t bufsz, offset, dropped; ssize_t readlen, left; int eof; }; 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; tnc->offset = 0; tnc->dropped = 0; tnc->readlen = 0; tnc->left = 0; tnc->eof = 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; } 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); free(tnc->buf); free(tnc); } static void tnc_drop(patty_kiss_tnc *tnc) { tnc->offset = 0; tnc->readlen = 0; tnc->left = 0; tnc->eof = 0; tnc->dropped++; } size_t patty_kiss_tnc_dropped(patty_kiss_tnc *tnc) { return tnc->dropped; } 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 */ enum kiss_flags flags = KISS_NONE; if (tnc->eof) { 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) { if (errno) { goto error_io; } goto done; } tnc->left = tnc->readlen; } c = ((uint8_t *)tnc->buf)[tnc->offset++]; if (tnc->offset == tnc->bufsz) { 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) { if (w > 0) { flags &= ~KISS_FRAME; goto done; } continue; } } if (!(flags & KISS_COMMAND)) { if (PATTY_KISS_COMMAND(c) != PATTY_KISS_DATA) { 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)[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); return 0; } tnc->left -= r; if (tnc->readlen < tnc->bufsz && tnc->left == 0) { tnc->eof = 1; } return (ssize_t)w; 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; ifd, ((uint8_t *)buf) + start, end - start) < 0) { goto error_io; } 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; } } if (write_byte(tnc->fd, PATTY_KISS_FEND) < 0) { goto error_io; } return len; error_io: return -1; }