From 94af7aa13b923fb96c152c4cb4251fe415271a91 Mon Sep 17 00:00:00 2001
From: Matt Ranostay <matt.ranostay@konsulko.com>
Date: Thu, 10 Aug 2017 11:13:22 -0700
Subject: binding: gps: move build to cmake

Enable cmake build system for building webpack wgt, and remove
unnecessary files

Change-Id: Ia00b1ba7a9d26e7f8933c0ccc97bf8b91dbfa012
Bug-AGL: SPEC-823
Signed-off-by: Matt Ranostay <matt.ranostay@konsulko.com>
---
 binding/CMakeLists.txt    |   39 ++
 binding/afm-gps-binding.c | 1051 +++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 1090 insertions(+)
 create mode 100644 binding/CMakeLists.txt
 create mode 100644 binding/afm-gps-binding.c

(limited to 'binding')

diff --git a/binding/CMakeLists.txt b/binding/CMakeLists.txt
new file mode 100644
index 0000000..ceb48d9
--- /dev/null
+++ b/binding/CMakeLists.txt
@@ -0,0 +1,39 @@
+###########################################################################
+# Copyright 2015, 2016, 2017 IoT.bzh
+#
+# author: Fulup Ar Foll <fulup@iot.bzh>
+# contrib: Romain Forlot <romain.forlot@iot.bzh>
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+###########################################################################
+
+# Add target to project dependency list
+PROJECT_TARGET_ADD(afm-gps-binding)
+
+	# Define project Targets
+	add_library(afm-gps-binding MODULE afm-gps-binding.c)
+
+	# Binder exposes a unique public entry point
+	SET_TARGET_PROPERTIES(${TARGET_NAME} PROPERTIES
+		LABELS "BINDING"
+		LINK_FLAGS ${BINDINGS_LINK_FLAG}
+		OUTPUT_NAME ${TARGET_NAME}
+	)
+
+	# Library dependencies (include updates automatically)
+	TARGET_LINK_LIBRARIES(${TARGET_NAME} ${link_libraries})
+
+	# installation directory
+	INSTALL(TARGETS ${TARGET_NAME}
+		LIBRARY DESTINATION ${BINDINGS_INSTALL_DIR})
+
diff --git a/binding/afm-gps-binding.c b/binding/afm-gps-binding.c
new file mode 100644
index 0000000..080f1e5
--- /dev/null
+++ b/binding/afm-gps-binding.c
@@ -0,0 +1,1051 @@
+/*
+ * Copyright (C) 2016 "IoT.bzh"
+ * Author José Bollo <jose.bollo@iot.bzh>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <netdb.h>
+#include <fcntl.h>
+#include <math.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <json-c/json.h>
+
+#include <systemd/sd-event.h>
+
+#include <afb/afb-binding.h>
+#include <afb/afb-service-itf.h>
+
+#define NAUTICAL_MILE_IN_METER                     1852
+#define MILE_IN_METER                              1609.344
+#define KNOT_TO_METER_PER_SECOND                   0.5144444444         /* 1852 / 3600 */
+#define METER_PER_SECOND_TO_KNOT                   1.943844492          /* 3600 / 1852 */
+#define METER_PER_SECOND_TO_KILOMETER_PER_HOUR     3.6                  /* 3600 / 1000 */
+#define METER_PER_SECOND_TO_MILE_PER_HOUR          2.236936292          /* 3600 / 1609.344 */
+
+#define DEFAULT_PERIOD   2000   /* 2 seconds */
+
+/*
+ * references:
+ *
+ *       https://www.w3.org/TR/geolocation-API/
+ *       http://www.gpsinformation.org/dale/nmea.htm
+ */
+
+/* flags for recording what field is set */
+struct flags {
+	unsigned time: 1;
+	unsigned latitude: 1;
+	unsigned longitude: 1;
+	unsigned altitude: 1;
+	unsigned speed: 1;
+	unsigned track: 1;
+};
+
+/* the gps data converted */
+struct gps {
+	struct flags set;
+
+	uint32_t time;
+	double latitude;
+	double longitude;
+	double altitude;
+	double speed;
+	double track;
+};
+
+/*
+ * the type of position expected
+ *
+ * here, this type is mainly the selection of units
+ */
+enum type {
+	type_wgs84,	/* longitude, latitude, track: degre, altitude: m, speed: m/s */
+	type_dms_kmh,	/* longitude, latitude: degre°minute'second.xxx"X, track: degre, altitude: m, speed: km/h */
+	type_dms_mph,	/* longitude, latitude: degre°minute'second.xxx"X, track: degre, altitude: m, speed: mph  */
+	type_dms_kn,	/* longitude, latitude: degre°minute'second.xxx"X, track: degre, altitude: m, speed: kn   */
+	type_COUNT,
+	type_DEFAULT = type_wgs84,
+	type_INVALID = -1
+};
+
+struct event;
+
+/*
+ * for each expected period
+ */
+struct period {
+	struct period *next;	/* link to the next other period */
+	struct event *events;	/* events for the period */
+	uint32_t period;	/* value of the period in ms */
+	uint32_t last;		/* last update of the period */
+};
+
+/*
+ * each generated event
+ */
+struct event {
+	struct event *next;	/* link for the same period */
+	const char *name;	/* name of the event */
+	struct afb_event event;	/* the event for the binder */
+	enum type type;		/* the type of data expected */
+	int id;			/* id of the event for unsubscribe */
+};
+
+/*
+ * names of the types
+ */
+static const char * const type_NAMES[type_COUNT] = {
+	"WGS84",
+	"DMS.km/h",
+	"DMS.mph",
+	"DMS.kn"
+};
+
+/*
+ * the interface to afb-daemon
+ */
+const struct afb_binding_interface *afbitf;
+
+/*
+ * records the raw frames
+ */
+static struct gps frames[10];	/* a short memory for further computation if needed */
+static int frameidx;		/* index of the last frame (frames are in the reverse order) */
+static int newframes;		/* boolean indication of wether new frames are availables */
+
+/*
+ * records the JSON object for sending positions
+ */
+static struct json_object *time_ms;		/* time as double in millisecond */
+static struct json_object *latitude_wgs;	/* latitude as double in degree */
+static struct json_object *longitude_wgs;	/* longitude as double in degree */
+static struct json_object *latitude_dms;	/* latitude as string in d°m's.s"X */
+static struct json_object *longitude_dms;	/* longitude as string in d°m's.s"X */
+static struct json_object *altitude_m;		/* altitude as double in meter */
+static struct json_object *speed_ms;		/* speed as double in m/s */
+static struct json_object *speed_kmh;		/* speed as double in km/h */
+static struct json_object *speed_mph;		/* speed as double in mph */
+static struct json_object *speed_kn;		/* speed as double in kn */
+static struct json_object *track_d;		/* heading track as double in degree */
+
+static struct json_object *positions[type_COUNT];	/* computed positions by type */
+
+/* head of the list of periods */
+static struct period *list_of_periods;
+
+/***************************************************************************************/
+/***************************************************************************************/
+/**                                                                                   **/
+/**                                                                                   **/
+/**       SECTION: FORMATING JSON POSITIONS                                           **/
+/**                                                                                   **/
+/**                                                                                   **/
+/***************************************************************************************/
+/***************************************************************************************/
+/*
+ * Creates the JSON representation for Degree Minute Second representation of coordinates
+ */
+static struct json_object *new_dms(double a, int islat)
+{
+	char buffer[50], pos;
+	double D, M;
+
+	if (islat) {
+		if (a >= 0)
+			pos = 'N';
+		else {
+			a = -a;
+			pos = 'S';
+		}
+	} else {
+		if (a <= 180)
+			pos = 'E';
+		else {
+			a = 360 - a;
+			pos = 'W';
+		}
+	}
+	D = floor(a);
+	a = (a - D) * 60;
+	M = floor(a);
+	a = (a - M) * 60;
+	sprintf(buffer, "%d°%d'%.3f\"%c", (int)D, (int)M, a, pos);
+	return json_object_new_string(buffer);
+}
+
+/*
+ * adds the value (with reference count increment) if not null
+ */
+static void addif(struct json_object *obj, const char *name, struct json_object *val)
+{
+	if (val != NULL)
+		json_object_object_add(obj, name, json_object_get(val));
+}
+
+/*
+ * release the object (put) and reset the pointer to null
+ */
+static void clear(struct json_object **obj)
+{
+	json_object_put(*obj);
+	*obj = NULL;
+}
+
+/*
+ * get the last/current position of type
+ */
+static struct json_object *position(enum type type)
+{
+	struct json_object *result;
+	struct gps *g0;
+
+	/* clean on new frame */
+	if (newframes) {
+		clear(&time_ms);
+		clear(&latitude_wgs);
+		clear(&longitude_wgs);
+		clear(&latitude_dms);
+		clear(&longitude_dms);
+		clear(&altitude_m);
+		clear(&speed_ms);
+		clear(&speed_kmh);
+		clear(&speed_mph);
+		clear(&speed_kn);
+		clear(&track_d);
+		clear(&positions[type_wgs84]);
+		clear(&positions[type_dms_kmh]);
+		clear(&positions[type_dms_mph]);
+		clear(&positions[type_dms_kn]);
+		newframes = 0;
+	}
+
+	/* get the result */
+	result = positions[type];
+	if (result == NULL) {
+		DEBUG(afbitf, "building position for type %s", type_NAMES[type]);
+
+		/* should build the result */
+		g0 = &frames[frameidx];
+		result = json_object_new_object();
+		if (result == NULL)
+			return NULL;
+		positions[type] = result;
+
+		/* set the result type */
+		json_object_object_add(result, "type", json_object_new_string(type_NAMES[type]));
+
+		/* build time, altitude and track */
+		if (time_ms == NULL && g0->set.time)
+			time_ms = json_object_new_double (g0->time);
+		addif(result, "time", time_ms);
+		if (altitude_m == NULL && g0->set.altitude)
+			altitude_m = json_object_new_double (g0->altitude);
+		addif(result, "altitude", altitude_m);
+		if (track_d == NULL && g0->set.track)
+			track_d = json_object_new_double (g0->track);
+		addif(result, "track", track_d);
+
+		/* build position */
+		switch (type) {
+		default:
+		case type_wgs84:
+			if (latitude_wgs == NULL && g0->set.latitude)
+				latitude_wgs = json_object_new_double (g0->latitude);
+			addif(result, "latitude", latitude_wgs);
+			if (longitude_wgs == NULL && g0->set.longitude)
+				longitude_wgs = json_object_new_double (g0->longitude);
+			addif(result, "longitude", longitude_wgs);
+			break;
+		case type_dms_kmh:
+		case type_dms_mph:
+		case type_dms_kn:
+			if (latitude_dms == NULL && g0->set.latitude)
+				latitude_dms = new_dms (g0->latitude, 1);
+			addif(result, "latitude", latitude_dms);
+			if (longitude_dms == NULL && g0->set.longitude)
+				longitude_dms = new_dms (g0->longitude, 0);
+			addif(result, "longitude", longitude_dms);
+			break;
+		}
+
+		/* build speed */
+		switch (type) {
+		default:
+		case type_wgs84:
+			if (speed_ms == NULL && g0->set.speed)
+				speed_ms = json_object_new_double (g0->speed);
+			addif(result, "speed", speed_ms);
+			break;
+		case type_dms_kmh:
+			if (speed_kmh == NULL && g0->set.speed)
+				speed_kmh = json_object_new_double (g0->speed * METER_PER_SECOND_TO_KILOMETER_PER_HOUR);
+			addif(result, "speed", speed_kmh);
+			break;
+		case type_dms_mph:
+			if (speed_mph == NULL && g0->set.speed)
+				speed_mph = json_object_new_double (g0->speed * METER_PER_SECOND_TO_MILE_PER_HOUR);
+			addif(result, "speed", speed_mph);
+			break;
+		case type_dms_kn:
+			if (speed_kn == NULL && g0->set.speed)
+				speed_kn = json_object_new_double (g0->speed * METER_PER_SECOND_TO_KNOT);
+			addif(result, "speed", speed_kn);
+			break;
+		}
+	}
+
+	return json_object_get(result);
+}
+
+/***************************************************************************************/
+/***************************************************************************************/
+/**                                                                                   **/
+/**                                                                                   **/
+/**       SECTION: MANAGING EVENTS                                                    **/
+/**                                                                                   **/
+/**                                                                                   **/
+/***************************************************************************************/
+/***************************************************************************************/
+/*
+ * get the event handler of given id
+ */
+static struct event *event_of_id(int id)
+{
+	struct period *p;
+	struct event *e;
+
+	p = list_of_periods;
+	while(p != NULL) {
+		e = p->events;
+		p = p->next;
+		while(e != NULL) {
+			if (e->id == id)
+				return e;
+			e = e->next;
+		}
+	}
+	return NULL;
+}
+
+/*
+ * get the event handler for the type and the period
+ */
+static struct event *event_get(enum type type, int period)
+{
+	static int id;
+	int shift;
+	uint32_t perio;
+	struct period *p, **pp, *np;
+	struct event *e;
+
+	/* normalize the period */
+	period = period <= 100 ? 1 : period > 60000 ? 600 : (period / 100);
+	shift = 0;
+	while((period >> shift) > 31)
+		shift++;
+	perio = (uint32_t)(100 * (((period >> shift) & 31) << shift));
+
+	/* search for the period */
+	pp = &list_of_periods;
+	p = *pp;
+	while(p != NULL && p->period < perio) {
+		pp = &p->next;
+		p = *pp;
+	}
+
+	/* create the period if it misses */
+	if (p == NULL || p->period != perio) {
+		np = calloc(1, sizeof *p);
+		if (np == NULL)
+			return NULL;
+		np->next = p;
+		np->period = perio;
+		*pp = np;
+		p = np;
+	}
+
+	/* search the type */
+	e = p->events;
+	while(e != NULL && e->type != type)
+		e = e->next;
+
+	/* creates the type if needed */
+	if (e == NULL) {
+		e = calloc(1, sizeof *e);
+		if (e == NULL)
+			return NULL;
+
+		e->name = "GPS"; /* TODO */
+		e->event = afb_daemon_make_event(afbitf->daemon, e->name);
+		if (e->event.itf == NULL) {
+			free(e);
+			return NULL;
+		}
+
+		e->next = p->events;
+		e->type = type;
+		do {
+			id++;
+			if (id < 0)
+				id = 1;
+		} while(event_of_id(id) != NULL);
+		e->id = id;
+		p->events = e;
+	}
+
+	return e;
+}
+
+/*
+ * Sends the events if needed
+ */
+static void event_send()
+{
+	struct period *p, **pp;
+	struct event *e, **pe;
+	struct timeval tv;
+	uint32_t now;
+
+	/* skip if nothing is new */
+	if (!newframes)
+		return;
+
+	/* computes now */
+	gettimeofday(&tv, NULL);
+	now = (uint32_t)(tv.tv_sec * 1000) + (uint32_t)(tv.tv_usec / 1000);
+
+	/* iterates over the periods */
+	pp = &list_of_periods;
+	p = *pp;
+	while (p != NULL) {
+		if (p->events == NULL) {
+			/* no event for the period, frees it */
+			*pp = p->next;
+			free(p);
+		} else {
+			if (p->period <= now - p->last) {
+				/* its time to refresh */
+				p->last = now;
+				pe = &p->events;
+				e = *pe;
+				while (e != NULL) {
+					/* sends the event */
+					if (afb_event_push(e->event, position(e->type)) != 0)
+						pe = &e->next;
+					else {
+						/* no more listeners, free the event */
+						*pe = e->next;
+						afb_event_drop(e->event);
+						free(e);
+					}
+					e = *pe;
+				}
+			}
+			pp = &p->next;
+		}
+		p = *pp;
+	}
+}
+
+/***************************************************************************************/
+/***************************************************************************************/
+/**                                                                                   **/
+/**                                                                                   **/
+/**       SECTION: HANDLING NMEA                                                      **/
+/**                                                                                   **/
+/**                                                                                   **/
+/***************************************************************************************/
+/***************************************************************************************/
+/*
+ * interprets a nmea time
+ */
+static int nmea_time(const char *text, uint32_t *result)
+{
+	uint32_t x;
+
+	if (text[0] < '0' || text[0] > '2'
+	 || text[1] < '0' || text[1] > (text[0] == '2' ? '3' : '9')
+	 || text[2] < '0' || text[2] > '5'
+	 || text[3] < '0' || text[3] > '9'
+	 || text[4] < '0' || text[4] > '5'
+	 || text[5] < '0' || text[5] > '9'
+	 || (text[6] != 0 && text[6] != '.'))
+		return 0;
+
+	x = (uint32_t)(text[0] - '0');
+	x = x * 10 + (uint32_t)(text[1]-'0');
+	x = x *  6 + (uint32_t)(text[2]-'0');
+	x = x * 10 + (uint32_t)(text[3]-'0');
+	x = x *  6 + (uint32_t)(text[4]-'0');
+	x = x * 10 + (uint32_t)(text[5]-'0');
+	x = x * 1000;
+	if (text[6] == '.') {
+		if (text[7] != 0) {
+			if (text[7] < '0' || text[7] > '9') return 0;
+			x += (uint32_t)(text[7]-'0') * 100;
+			if (text[8] != 0) {
+				if (text[8] < '0' || text[8] > '9') return 0;
+				x += (uint32_t)(text[8]-'0') * 10;
+				if (text[9] != 0) {
+					if (text[9] < '0' || text[9] > '9') return 0;
+					x += (uint32_t)(text[9]-'0');
+					if (text[10] != 0) {
+						if (text[10] < '0' || text[10] > '9') return 0;
+						x += text[10] > '5';
+					}
+				}
+			}
+		}
+	}
+
+	*result = x;
+	return 1;
+}
+
+/*
+ * interprets a nmea angle having minutes
+ */
+static int nmea_angle(const char *text, double *result)
+{
+	uint32_t x = 0;
+	double v;
+	int dotidx = (int)(strchrnul(text, '.') - text);
+
+	switch(dotidx) {
+	case 5:
+		if (text[dotidx - 5] < '0' || text[dotidx - 5] > '9')
+			return 0;
+		x = x * 10 + (uint32_t)(text[dotidx - 5] - '0');
+	case 4:
+		if (text[dotidx - 4] < '0' || text[dotidx - 4] > '9')
+			return 0;
+		x = x * 10 + (uint32_t)(text[dotidx - 4] - '0');
+	case 3:
+		if (text[dotidx - 3] < '0' || text[dotidx - 3] > '9')
+			return 0;
+		x = x * 10 + (uint32_t)(text[dotidx - 3] - '0');
+	case 2:
+		v = atof(&text[dotidx - 2]);
+		break;
+	case 1:
+		if (text[dotidx - 1] < '0' || text[dotidx - 1] > '9')
+			return 0;
+	case 0:
+		v = atof(text);
+		break;
+	default:
+		return 0;
+	}
+
+	*result = (double)x + v * 0.01666666666666666666666; /* 1 / 60 */
+
+	return 1;
+}
+
+/*
+ * creates a new position for the given optionnal fields
+ * returns 1 if correct or 0 if a format error exists
+ */
+static int nmea_set(
+		const char *tim,
+		const char *lat, const char *latu,
+		const char *lon, const char *lonu,
+		const char *alt, const char *altu,
+		const char *spe,
+		const char *tra,
+		const char *dat
+)
+{
+	struct gps gps;
+
+	DEBUG(afbitf, "time=%s latitude=%s%s longitude=%s%s altitude=%s%s speed=%s track=%s date=%s",
+		tim, lat, latu, lon, lonu, alt, altu, spe, tra, dat);
+
+	/* get the time in milliseconds */
+	if (tim == NULL)
+		gps.set.time = 0;
+	else {
+		if (!nmea_time(tim, &gps.time))
+			return 0;
+		gps.set.time = 1;
+	}
+
+	/* get the latitude */
+	if (lat == NULL || latu == NULL)
+		gps.set.latitude = 0;
+	else {
+		if ((latu[0] != 'N' && latu[0] != 'S') || latu[1] != 0)
+			return 0;
+		if (!nmea_angle(lat, &gps.latitude))
+			return 0;
+		if (latu[0] == 'S')
+			gps.latitude = -gps.latitude;
+		gps.set.latitude = 1;
+	}
+
+	/* get the longitude */
+	if (lon == NULL || lonu == NULL)
+		gps.set.longitude = 0;
+	else {
+		if ((lonu[0] != 'E' && lonu[0] != 'W') || lonu[1] != 0)
+			return 0;
+		if (!nmea_angle(lon, &gps.longitude))
+			return 0;
+		if (lonu[0] == 'W')
+			gps.longitude = 360.0 - gps.longitude;
+		gps.set.longitude = 1;
+	}
+
+	/* get the altitude */
+	if (alt == NULL || altu == NULL)
+		gps.set.altitude = 0;
+	else {
+		if (altu[0] != 'M' || altu[1] != 0)
+			return 0;
+		gps.altitude = atof(alt);
+		gps.set.altitude = 1;
+	}
+
+	/* get the speed */
+	if (spe == NULL)
+		gps.set.speed = 0;
+	else {
+		gps.speed = atof(spe) * KNOT_TO_METER_PER_SECOND;
+		gps.set.speed = 1;
+	}
+
+	/* get the track */
+	if (tra != NULL)
+		gps.set.track = 0;
+	else {
+		gps.track = atof(tra);
+		gps.set.track = 1;
+	}
+
+	/* push the frame */
+	frameidx = (frameidx ? : (int)(sizeof frames / sizeof *frames)) - 1; 
+	frames[frameidx] = gps;
+	newframes++;
+
+	DEBUG(afbitf, "time:%d=%d latitude:%d=%g longitude:%d=%g altitude:%d=%g speed:%d=%g track:%d=%g",
+		(int)gps.set.time, gps.set.time ? (int)gps.time : 0,
+		(int)gps.set.latitude, gps.set.latitude ? gps.latitude : 0,
+		(int)gps.set.longitude, gps.set.longitude ? gps.longitude : 0,
+		(int)gps.set.altitude, gps.set.altitude ? gps.altitude : 0,
+		(int)gps.set.speed, gps.set.speed ? gps.speed : 0,
+		(int)gps.set.track, gps.set.track ? gps.track : 0
+	);
+
+	return 1;
+}
+
+/*
+ * Splits the nmea sentences in its fields
+ */
+static int nmea_split(char *s, char *fields[], int count)
+{
+	int index = 0;
+	while (*s && index < count) {
+		fields[index++] = s;
+		while (*s && *s != ',')
+			s++;
+		if (*s == ',')
+			*s++ = 0;
+	}
+	return !*s && index == count;
+}
+
+/*
+ * interprete one sentence GGA - Fix information
+ */
+static int nmea_gga(char *s)
+{
+	char *f[14];
+
+	return nmea_split(s, f, (int)(sizeof f / sizeof *f))
+		&& *f[5] != '0'
+		&&  nmea_set(f[0], f[1], f[2], f[3], f[4], f[6], f[7], NULL, NULL, NULL);
+}
+
+/*
+ * interprete one sentence RMC - Recommended Minimum
+ */
+static int nmea_rmc(char *s)
+{
+	char *f[12];
+
+	return nmea_split(s, f, (int)(sizeof f / sizeof *f))
+		&& *f[1] == 'A'
+		&&  nmea_set(f[0], f[2], f[3], f[4], f[5], NULL, NULL, f[6], f[7], f[8]);
+}
+
+
+/*
+ * interprete one NMEA sentence
+ */
+static int nmea_sentence(char *s)
+{
+	if (!s[0] || !s[1])
+		return 0;
+
+	if (s[2] == 'G' && s[3] == 'G' && s[4] == 'A' && s[5] == ',')
+		return nmea_gga(&s[6]);
+
+	if (s[2] == 'R' && s[3] == 'M' && s[4] == 'C' && s[5] == ',')
+		return nmea_rmc(&s[6]);
+
+	return 0;
+}
+
+/*
+ * reads the NMEA stream
+ */
+static int nmea_read(int fd)
+{
+	static char buffer[160];
+	static int pos = 0;
+	static int overflow = 0;
+
+	int rc;
+
+	for(;;) {
+		rc = (int)read(fd, &buffer[pos], sizeof buffer - (size_t)pos);
+		if (rc < 0) {
+			/* its an error if not interrupted */
+			if (errno != EINTR)
+				return rc;
+		} else if (rc == 0) {
+			/* nothing more to be read */
+			return 0;
+		} else {
+			/* scan the buffer */
+			while (pos != rc) {
+				if (buffer[pos] != '\n') {
+					pos++;
+					if (pos == rc) {
+						if (pos == (int)(sizeof buffer)) {
+							overflow = 1;
+							pos = 0;
+							rc = 0;
+						}
+					}
+				} else {
+					if (buffer[0] == '$' && pos > 0 && buffer[pos-1] == '\r' && !overflow) {
+						if (pos > 3 && buffer[pos-4] == '*') {
+							/* TODO: check the cheksum */
+							buffer[pos-4] = 0;
+						} else {
+							buffer[pos-1] = 0;
+						}
+						nmea_sentence(&buffer[1]);
+					}
+					pos++;
+					rc -= pos;
+					if (rc > 0)
+						memmove(buffer, buffer+pos, (size_t)rc);
+					pos = 0;
+					overflow = 0;
+				}
+			}
+		}
+	}
+}
+
+/***************************************************************************************/
+/***************************************************************************************/
+/**                                                                                   **/
+/**                                                                                   **/
+/**       SECTION: HANDLING OF CONNECTION                                             **/
+/**                                                                                   **/
+/**                                                                                   **/
+/***************************************************************************************/
+/***************************************************************************************/
+/* declare the connection routine */
+static int connection();
+
+/*
+ * called on an event on the NMEA stream
+ */
+static int on_event(sd_event_source *s, int fd, uint32_t revents, void *userdata)
+{
+	/* read available data */
+	if ((revents & EPOLLIN) != 0) {
+		nmea_read(fd);
+		event_send();
+	}
+
+	/* check if error or hangup */
+	if ((revents & (EPOLLERR|EPOLLRDHUP|EPOLLHUP)) != 0) {
+		sd_event_source_unref(s);
+		close(fd);
+		connection(fd);
+	}
+
+	return 0;
+}
+
+/*
+ * opens a socket to a host and a service (or port)
+ */
+static int open_socket_to(const char *host, const char *service)
+{
+	int rc, fd;
+	struct addrinfo hint, *rai, *iai;
+
+	/* get addr */
+	memset(&hint, 0, sizeof hint);
+	hint.ai_family = AF_INET;
+	hint.ai_socktype = SOCK_STREAM;
+	rc = getaddrinfo(host, service, &hint, &rai);
+	if (rc != 0)
+		return -1;
+
+	/* get the socket */
+	iai = rai;
+	while (iai != NULL) {
+		fd = socket(iai->ai_family, iai->ai_socktype, iai->ai_protocol);
+		if (fd >= 0) {
+			rc = connect(fd, iai->ai_addr, iai->ai_addrlen);
+			if (rc == 0) {
+				fcntl(fd, F_SETFL, O_NONBLOCK);
+				freeaddrinfo(rai);
+				return fd;
+			}
+			close(fd);
+		}
+		iai = iai->ai_next;
+	}
+	freeaddrinfo(rai);
+	return -1;
+}
+
+/*
+ * connection to nmea stream for the host and the port
+ */
+static int connect_to(const char *host, const char *service, int isgpsd)
+{
+	sd_event_source *source;
+	int rc, fd;
+
+	/* TODO connect to somewhere else */
+	fd = open_socket_to(host, service);
+	if (fd < 0) {
+		ERROR(afbitf, "can't connect to host %s, service %s", host, service);
+		return fd;
+	}
+	if (isgpsd) {
+		static const char gpsdsetup[] = "?WATCH={\"enable\":true,\"nmea\":true};\r\n";
+		write(fd, gpsdsetup, sizeof gpsdsetup - 1);
+	}
+
+	/* adds to the event loop */
+	rc = sd_event_add_io(afb_daemon_get_event_loop(afbitf->daemon), &source, fd, EPOLLIN, on_event, NULL);
+	if (rc < 0) {
+		close(fd);
+		ERROR(afbitf, "can't coonect host %s, service %s to the event loop", host, service);
+	} else {
+		NOTICE(afbitf, "Connected to host %s, service %s", host, service);
+	}
+	return rc;
+}
+
+/*
+ * connection to nmea stream
+ */
+static int connection()
+{
+	const char *host;
+	const char *service;
+	int isgpsd;
+
+	/* TODO connect to somewhere else */
+	host = getenv("AFBGPS_HOST") ? : "sinagot.net";
+	service = getenv("AFBGPS_SERVICE") ? : "5001";
+	isgpsd = getenv("AFBGPS_ISNMEA") ? 0 : 1;
+	return connect_to(host, service, isgpsd);
+}
+
+/***************************************************************************************/
+/***************************************************************************************/
+/**                                                                                   **/
+/**                                                                                   **/
+/**       SECTION: BINDING VERBS IMPLEMENTATION                                       **/
+/**                                                                                   **/
+/**                                                                                   **/
+/***************************************************************************************/
+/***************************************************************************************/
+/*
+ * Returns the type corresponding to the given name
+ */
+static enum type type_of_name(const char *name)
+{
+	enum type result;
+	if (name == NULL)
+		return type_DEFAULT;
+	for (result = 0 ; result != type_COUNT ; result++)
+		if (strcmp(type_NAMES[result], name) == 0)
+			return result;
+	return type_INVALID;
+}
+
+/*
+ * extract a valid type from the request
+ */
+static int get_type_for_req(struct afb_req req, enum type *type)
+{
+	if ((*type = type_of_name(afb_req_value(req, "type"))) != type_INVALID)
+		return 1;
+	afb_req_fail(req, "unknown-type", NULL);
+	return 0;
+}
+	
+/*
+ * Get the last known position
+ *
+ * parameter of the get are:
+ *
+ *    type:   string: the type of position expected (defaults to "WGS84" if not present)
+ *
+ * returns the position
+ *
+ * The valid types are:
+ *
+ *  +==========+=======================+=======+==========+=======+
+ *  | type     |  longitude & latitude | speed | altitude | track |
+ *  +==========+=======================+=======+==========+=======+
+ *  | WGS84    |      degre            |  m/s  |          |       |
+ *  +----------+-----------------------+-------+          |       |
+ *  | DMS.km/h |                       | km/h  |          |       |
+ *  +----------+                       +-------+   meter  | degre |
+ *  | DMS.mph  |   deg°min'sec"X       |  mph  |          |       |
+ *  +----------+                       +-------+          |       |
+ *  | DMS.kn   |                       |  kn   |          |       |
+ *  +==========+=======================+=======+==========+=======+
+ */
+static void get(struct afb_req req)
+{
+	enum type type;
+	if (get_type_for_req(req, &type))
+		afb_req_success(req, position(type), NULL);
+}
+
+/*
+ * subscribe to notification of position
+ *
+ * parameters of the subscription are:
+ *
+ *    type:   string:  the type of position expected (defaults to WCS84 if not present)
+ *                     see the list above (get)
+ *    period: integer: the expected period in milliseconds (defaults to 2000 if not present)
+ *
+ * returns an object with 2 fields:
+ *
+ *    name:   string:  the name of the event without its prefix
+ *    id:     integer: a numeric identifier of the event to be used for unsubscribing
+ */
+static void subscribe(struct afb_req req)
+{
+	enum type type;
+	const char *period;
+	struct event *event;
+	struct json_object *json;
+
+	if (get_type_for_req(req, &type)) {
+		period = afb_req_value(req, "period");
+		event = event_get(type, period == NULL ? DEFAULT_PERIOD : atoi(period));
+		if (event == NULL)
+			afb_req_fail(req, "out-of-memory", NULL);
+		else if (afb_req_subscribe(req, event->event) != 0)
+			afb_req_fail_f(req, "failed", "afb_req_subscribe returned an error: %m");
+		else {
+			json = json_object_new_object();
+			json_object_object_add(json, "name", json_object_new_string(event->name));
+			json_object_object_add(json, "id", json_object_new_int(event->id));
+			afb_req_success(req, json, NULL);
+		}
+	}
+}
+
+/*
+ * unsubscribe a previous subscription
+ *
+ * parameters of the unsubscription are:
+ *
+ *    id:   integer: the numeric identifier of the event as returned when subscribing
+ */
+static void unsubscribe(struct afb_req req)
+{
+	const char *id;
+	struct event *event;
+
+	id = afb_req_value(req, "id");
+	if (id == NULL)
+		afb_req_fail(req, "missing-id", NULL);
+	else {
+		event = event_of_id(atoi(id));
+		if (event == NULL)
+			afb_req_fail(req, "bad-id", NULL);
+		else {
+			afb_req_unsubscribe(req, event->event);
+			afb_req_success(req, NULL, NULL);
+		}
+	}
+}
+
+/*
+ * array of the verbs exported to afb-daemon
+ */
+static const struct afb_verb_desc_v1 binding_verbs[] = {
+  /* VERB'S NAME            SESSION MANAGEMENT          FUNCTION TO CALL         SHORT DESCRIPTION */
+  { .name= "get",          .session= AFB_SESSION_NONE, .callback= get,          .info= "get the last known data" },
+  { .name= "subscribe",    .session= AFB_SESSION_NONE, .callback= subscribe,    .info= "subscribe to notification of position" },
+  { .name= "unsubscribe",  .session= AFB_SESSION_NONE, .callback= unsubscribe,  .info= "unsubscribe a previous subscription" },
+  { .name= NULL } /* marker for end of the array */
+};
+
+/*
+ * description of the binding for afb-daemon
+ */
+static const struct afb_binding binding_description =
+{
+  /* description conforms to VERSION 1 */
+  .type= AFB_BINDING_VERSION_1,
+  .v1= {			/* fills the v1 field of the union when AFB_BINDING_VERSION_1 */
+    .prefix= "gps",		/* the API name (or binding name or prefix) */
+    .info= "Access to the GPS data",	/* short description of of the binding */
+    .verbs = binding_verbs	/* the array describing the verbs of the API */
+  }
+};
+
+/*
+ * activation function for registering the binding called by afb-daemon
+ */
+const struct afb_binding *afbBindingV1Register(const struct afb_binding_interface *itf)
+{
+	afbitf = itf;			/* records the interface for accessing afb-daemon */
+	return &binding_description;	/* returns the description of the binding */
+}
+
+int afbBindingV1ServiceInit(struct afb_service service)
+{
+	return connection();
+}
-- 
cgit