diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..4054440 --- /dev/null +++ b/Makefile @@ -0,0 +1,11 @@ +all: + $(MAKE) -C src all + $(MAKE) -C examples all + +install: + $(MAKE) -C src install + $(MAKE) -C examples install + +clean: + $(MAKE) -C src clean + $(MAKE) -C examples clean diff --git a/configure b/configure new file mode 100755 index 0000000..b3ad4a0 --- /dev/null +++ b/configure @@ -0,0 +1,100 @@ +#! /bin/sh + +OS=`uname -s` +DEBUG=0 + +create_linux_config_h() { + cat < src/config.h +#ifndef _CONFIG_H +#define _CONFIG_H +#include +#endif /* _CONFIG_H */ +EOF +} + +create_darwin_config_h() { + cat < src/config.h +#ifndef _CONFIG_H +#define _CONFIG_H +#include + +#ifdef __LITTLE_ENDIAN__ +#define __DO_SWAP_BYTES +#endif /* _DO_SWAP_BYTES */ +#endif /* _CONFIG_H */ +EOF +} + +create_common_build_mk() { + if [ "$DEBUG" = 1 ]; then + cat <<'EOF' > mk/build.mk +CGFLAGS = -g -fno-inline +EOF + else + cat <<'EOF' > mk/build.mk +CGFLAGS = +EOF + fi + + cat <<'EOF' >> mk/build.mk +CWFLAGS = -Wall +COFLAGS = -O2 +CFLAGS = $(CGFLAGS) $(CWFLAGS) $(COFLAGS) +EOF +} + +create_linux_build_mk() { + create_common_build_mk $@ + + cat <<'EOF' >> mk/build.mk +LLFLAGS = -shared -Wl,-soname=$(SONAME) + +STATIC = lib$(LIBNAME).a + +SONAME_SHORT = lib$(LIBNAME).so +SONAME = $(SONAME_SHORT).$(VERSION_MAJOR) +SONAME_FULL = $(SONAME_SHORT).$(VERSION) + +PREFIX = /usr/local +EOF +} + +create_darwin_build_mk() { + create_common_build_mk $@ + + cat <<'EOF' >> mk/build.mk +LLFLAGS = -dynamiclib -current_version $(VERSION) + +STATIC = lib$(LIBNAME).a + +SONAME_SHORT = lib$(LIBNAME).dylib +SONAME = lib$(LIBNAME).$(VERSION_MAJOR).dylib +SONAME_FULL = lib$(LIBNAME).$(VERSION).dylib + +PREFIX = /usr/local +EOF +} + +for arg in $@; do + case $arg in + "--enable-debug") + DEBUG=1 + ;; + esac +done + +if [ ! -d "mk" ]; then + mkdir -m 0755 mk +fi + +case $OS in + Linux) + create_linux_config_h + create_linux_build_mk + ;; + + Darwin) + create_darwin_config_h + create_darwin_build_mk + ;; +esac diff --git a/examples/Makefile b/examples/Makefile new file mode 100644 index 0000000..a4d2f4d --- /dev/null +++ b/examples/Makefile @@ -0,0 +1,20 @@ +include ../mk/build.mk + +CC = $(CROSS)cc + +INCLUDE_PATH = ../include + +CFLAGS += -I$(INCLUDE_PATH) +LDFLAGS = -L../src -lskipstone + +EXAMPLES = read + +RM = /bin/rm + +all: $(EXAMPLES) + +$(EXAMPLES): %: %.c $(STATICLIB) + $(CC) $(CFLAGS) $< -o $@ $(LDFLAGS) + +clean: + $(RM) -f $(EXAMPLES) diff --git a/examples/read.c b/examples/read.c new file mode 100644 index 0000000..207d23e --- /dev/null +++ b/examples/read.c @@ -0,0 +1,45 @@ +#include +#include +#include + +#include + +static void usage(int argc, char **argv) { + fprintf(stderr, "usage: %s /dev/rfcommX\n", argv[0]); + exit(1); +} + +int main(int argc, char **argv) { + skipstone_watch_link *link; + void *buf; + uint16_t len, endpoint; + + if (argc != 2) { + usage(argc, argv); + } + + if ((buf = malloc(SKIPSTONE_MESSAGE_MAX_PAYLOAD)) == NULL) { + goto error_malloc_buf; + } + + if ((link = skipstone_watch_link_open_serial(argv[1])) == NULL) { + goto error_watch_link_open; + } + + while (skipstone_recv(link, buf, &len, &endpoint) >= 0) { + printf("Received message %hu bytes for endpoint %hu\n", + len, endpoint); + } + + perror("skipstone_recv()"); + + skipstone_watch_link_close(link); + + return 0; + +error_watch_link_open: + free(buf); + +error_malloc_buf: + return 1; +} diff --git a/examples/read.c-e b/examples/read.c-e new file mode 100644 index 0000000..207d23e --- /dev/null +++ b/examples/read.c-e @@ -0,0 +1,45 @@ +#include +#include +#include + +#include + +static void usage(int argc, char **argv) { + fprintf(stderr, "usage: %s /dev/rfcommX\n", argv[0]); + exit(1); +} + +int main(int argc, char **argv) { + skipstone_watch_link *link; + void *buf; + uint16_t len, endpoint; + + if (argc != 2) { + usage(argc, argv); + } + + if ((buf = malloc(SKIPSTONE_MESSAGE_MAX_PAYLOAD)) == NULL) { + goto error_malloc_buf; + } + + if ((link = skipstone_watch_link_open_serial(argv[1])) == NULL) { + goto error_watch_link_open; + } + + while (skipstone_recv(link, buf, &len, &endpoint) >= 0) { + printf("Received message %hu bytes for endpoint %hu\n", + len, endpoint); + } + + perror("skipstone_recv()"); + + skipstone_watch_link_close(link); + + return 0; + +error_watch_link_open: + free(buf); + +error_malloc_buf: + return 1; +} diff --git a/include/skipstone.h b/include/skipstone.h deleted file mode 100644 index 984e60c..0000000 --- a/include/skipstone.h +++ /dev/null @@ -1,23 +0,0 @@ -#ifndef _SKIPSTONE_H -#define _SKIPSTONE_H - -#include -#include - -#define SKIPSTONE_MESSAGE_MAX_PAYLOAD 4096 - -typedef struct _skipstone_message { - uint16_t size, endpoint; -} skipstone_message; - -typedef struct _skipstone_watch_link skipstone_watch_link; - -skipstone_watch_link *skipstone_watch_link_open_serial(const char *device); - -void skipstone_watch_close(skipstone_watch_link *link); - -int16_t skipstone_send(skipstone_watch_link *link, void *buf, uint16_t size); - -int16_t skipstone_recv(skipstone_watch_link *link, void *buf, uint16_t size); - -#endif /* _SKIPSTONE_H */ diff --git a/include/skipstone/skipstone.h b/include/skipstone/skipstone.h new file mode 100644 index 0000000..642e3af --- /dev/null +++ b/include/skipstone/skipstone.h @@ -0,0 +1,31 @@ +#ifndef _SKIPSTONE_H +#define _SKIPSTONE_H + +#include +#include + +#define SKIPSTONE_MESSAGE_MAX_PAYLOAD 4096 + +enum skipstone_message_endpoint { + SKIPSTONE_MESSAGE_ENDPOINT_NONE = 0, + SKIPSTONE_MESSAGE_ENDPOINT_FIRMWARE = 1, + SKIPSTONE_MESSAGE_ENDPOINT_TIME = 11, + SKIPSTONE_MESSAGE_ENDPOINT_VERSIONS = 16, + SKIPSTONE_MESSAGE_ENDPOINT_PHONE_VERSION = 17, + SKIPSTONE_MESSAGE_ENDPOINT_SYSTEM_MESSAGE = 18, + SKIPSTONE_MESSAGE_ENDPOINT_MUSIC_CONTROL = 32, + SKIPSTONE_MESSAGE_ENDPOINT_PHONE_CONTROL = 33, + +}; + +typedef struct _skipstone_watch_link skipstone_watch_link; + +skipstone_watch_link *skipstone_watch_link_open_serial(const char *device); + +int skipstone_watch_link_close(skipstone_watch_link *link); + +int skipstone_send(skipstone_watch_link *link, void *buf, uint16_t size, uint16_t endpoint); + +int skipstone_recv(skipstone_watch_link *link, void *buf, uint16_t *size, uint16_t *endpoint); + +#endif /* _SKIPSTONE_H */ diff --git a/src/Makefile b/src/Makefile new file mode 100644 index 0000000..2cb2a06 --- /dev/null +++ b/src/Makefile @@ -0,0 +1,58 @@ +include ../mk/build.mk + +INCLUDE_PATH = ../include +HEADER_SUBDIR = skipstone + +CC = $(CROSS)cc +CFLAGS += -fPIC -I$(INCLUDE_PATH) +LDFLAGS = + +HEADERS = skipstone.h + +OBJS = skipstone.o + +VERSION_MAJOR = 0 +VERSION_MINOR = 0.1 +VERSION = $(VERSION_MAJOR).$(VERSION_MINOR) + +LIBNAME = skipstone + +HEADERS_BUILD = $(addprefix $(INCLUDE_PATH)/$(HEADER_SUBDIR)/, $(HEADERS)) + +AR = $(CROSS)ar +RANLIB = $(CROSS)ranlib + +RM = /bin/rm +LN = /bin/ln +RMDIR = /bin/rmdir +INSTALL = /usr/bin/install + +all: $(STATIC) $(SONAME_FULL) $(SONAME) $(SONAME_SHORT) + +$(STATIC): $(OBJS) + $(AR) rc $(STATIC) $(OBJS) + $(RANLIB) $(STATIC) + +$(SONAME_FULL): $(OBJS) + $(CC) $(LLFLAGS) $(OBJS) $(LDFLAGS) -o $(SONAME_FULL) + +$(SONAME): $(SONAME_FULL) + $(LN) -s $< $@ + +$(SONAME_SHORT): $(SONAME_FULL) + $(LN) -s $< $@ + +$(OBJS): %.o: %.c $(HEADERS_BUILD) + $(CC) $(CFLAGS) -c $< + +install: $(SONAME_FULL) $(STATIC) + $(INSTALL) -d -m 0755 $(PREFIX)/lib + $(INSTALL) -c -m 0644 $(STATIC) $(PREFIX)/lib + $(INSTALL) -c -m 0755 $(SONAME_FULL) $(PREFIX)/lib + $(LN) -s -f $(SONAME_FULL) $(PREFIX)/lib/$(SONAME) + $(LN) -s -f $(SONAME_FULL) $(PREFIX)/lib/$(SONAME_SHORT) + $(INSTALL) -d -m 0755 $(PREFIX)/include/$(HEADER_SUBDIR) + $(INSTALL) -c -m 0644 $(HEADERS_BUILD) $(PREFIX)/include/$(HEADER_SUBDIR) + +clean: + $(RM) -f $(SONAME_SHORT) $(SONAME) $(SONAME_FULL) $(STATIC) $(OBJS) diff --git a/src/skipstone.c b/src/skipstone.c index b88ed5a..4459675 100644 --- a/src/skipstone.c +++ b/src/skipstone.c @@ -1,19 +1,36 @@ #include +#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 link_type; + enum skipstone_watch_link_type type; union { - int serial_fd; + 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; @@ -21,22 +38,127 @@ skipstone_watch_link *skipstone_watch_link_open_serial(const char *device) { 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; } -void skipstone_watch_close(skipstone_watch_link *link) { +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; } -int16_t skipstone_send(skipstone_watch_link *link, void *buf, uint16_t size) { +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; } -int16_t skipstone_recv(skipstone_watch_link *link, void *buf, uint16_t size) { +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; } diff --git a/src/skipstone.c-e b/src/skipstone.c-e index b88ed5a..4459675 100644 --- a/src/skipstone.c-e +++ b/src/skipstone.c-e @@ -1,19 +1,36 @@ #include +#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 link_type; + enum skipstone_watch_link_type type; union { - int serial_fd; + 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; @@ -21,22 +38,127 @@ skipstone_watch_link *skipstone_watch_link_open_serial(const char *device) { 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; } -void skipstone_watch_close(skipstone_watch_link *link) { +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; } -int16_t skipstone_send(skipstone_watch_link *link, void *buf, uint16_t size) { +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; } -int16_t skipstone_recv(skipstone_watch_link *link, void *buf, uint16_t size) { +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; }