#include #include #include #include #include #include #include #include #include enum skipstone_watch_link_type { SKIPSTONE_WATCH_LINK_SERIAL = 1 }; struct _skipstone_watch_link { enum skipstone_watch_link_type type; union { struct { int fd; struct termios attr_old, attr_new; } serial; }; }; typedef struct _skipstone_message_header { uint16_t size, endpoint; } skipstone_message_header; skipstone_watch_link *skipstone_watch_link_open_serial(const char *device) { skipstone_watch_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_watch_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_watch_link_close(skipstone_watch_link *link) { switch (link->type) { case SKIPSTONE_WATCH_LINK_SERIAL: { return _watch_link_close_serial(link); } default: break; } return -1; } int skipstone_send(skipstone_watch_link *link, void *buf, uint16_t size, uint16_t endpoint) { skipstone_message_header header; if (size > SKIPSTONE_MESSAGE_MAX_PAYLOAD) { goto error_toobig; } header.size = htobe16(size); header.endpoint = htobe16(endpoint); if (write(link->serial.fd, &header, sizeof(header)) < 0) { goto error_io; } if (write(link->serial.fd, buf, (size_t)size) < 0) { goto error_io; } return 0; error_io: error_toobig: return -1; } int skipstone_recv(skipstone_watch_link *link, void *buf, uint16_t *size, uint16_t *endpoint) { skipstone_message_header header; while (1) { 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); *endpoint = be16toh(header.endpoint); return 0; } error_io: return -1; }