#include #include #include #include #include #include #include #include #include #include #include enum skipstone_link_type { SKIPSTONE_WATCH_LINK_SERIAL = 1 }; struct _skipstone_link { enum skipstone_link_type type; union { struct { int fd; struct termios attr_old, attr_new; } serial; }; }; skipstone_link *skipstone_link_open_serial(const char *device) { skipstone_link *link; if ((link = malloc(sizeof(*link))) == NULL) { goto error_malloc_link; } link->type = SKIPSTONE_WATCH_LINK_SERIAL; if ((link->serial.fd = open(device, O_RDWR | O_NOCTTY)) < 0) { goto error_open; } if (tcgetattr(link->serial.fd, &link->serial.attr_old) < 0) { goto error_io; } memset(&link->serial.attr_new, 0, sizeof(struct termios)); link->serial.attr_new.c_cflag = CS8 | HUPCL; link->serial.attr_new.c_ispeed = B115200; link->serial.attr_new.c_ospeed = B115200; link->serial.attr_new.c_iflag = IGNPAR; link->serial.attr_new.c_oflag = 0; link->serial.attr_new.c_lflag = 0; link->serial.attr_new.c_cc[VTIME] = 0; link->serial.attr_new.c_cc[VMIN] = 1; if (tcsetattr(link->serial.fd, TCSANOW, &link->serial.attr_new) < 0) { goto error_io; } if (fcntl(link->serial.fd, F_SETFL, 0) < 0) { goto error_io; } return link; error_io: close(link->serial.fd); error_open: free(link); error_malloc_link: return NULL; } static int _watch_link_close_serial(skipstone_link *link) { if (tcsetattr(link->serial.fd, TCSANOW, &link->serial.attr_old) < 0) { goto error_io; } return close(link->serial.fd); error_io: return -1; } int skipstone_link_close(skipstone_link *link) { switch (link->type) { case SKIPSTONE_WATCH_LINK_SERIAL: { return _watch_link_close_serial(link); } default: break; } return -1; } int skipstone_link_send(skipstone_link *link, void *buf, uint16_t size, uint16_t id) { skipstone_message_header header; ssize_t wrlen; size_t remaining = (size_t)size, offset = 0; if (size > SKIPSTONE_MESSAGE_MAX_PAYLOAD) { goto error_toobig; } header.size = htobe16(size); header.id = htobe16(id); if ((wrlen = write(link->serial.fd, &header, sizeof(header))) < 0) { goto error_io; } while (remaining) { if ((wrlen = write(link->serial.fd, (uint8_t *)buf + offset, remaining)) < 0) { goto error_io; } remaining -= (size_t)wrlen; offset += (size_t)wrlen; } return 0; error_io: error_toobig: return -1; } int skipstone_link_recv(skipstone_link *link, void *buf, uint16_t *size, uint16_t *id) { skipstone_message_header header; ssize_t len_read; size_t len_wanted, offset = 0; if (read(link->serial.fd, &header, sizeof(header)) < 0) { goto error_io; } len_wanted = (size_t)be16toh(header.size); if (len_wanted > SKIPSTONE_MESSAGE_MAX_PAYLOAD) { goto error_io; } while (len_wanted) { if ((len_read = read(link->serial.fd, (uint8_t *)buf + offset, len_wanted)) < 0) { goto error_io; } len_wanted -= len_read; offset += len_read; } *size = be16toh(header.size); *id = be16toh(header.id); return 0; error_io: return -1; }