diff options
| -rw-r--r-- | cethcan/.gitignore | 2 | ||||
| -rw-r--r-- | cethcan/Makefile | 14 | ||||
| -rw-r--r-- | cethcan/can.c | 65 | ||||
| -rw-r--r-- | cethcan/cethcan.h | 85 | ||||
| -rw-r--r-- | cethcan/ether.c | 157 | ||||
| -rw-r--r-- | cethcan/http.c | 143 | ||||
| -rw-r--r-- | cethcan/light.c | 93 | ||||
| -rw-r--r-- | cethcan/main.c | 68 | ||||
| -rw-r--r-- | cethcan/protocol.h | 24 | ||||
| -rw-r--r-- | cethcan/socketcan.c | 134 | 
10 files changed, 785 insertions, 0 deletions
diff --git a/cethcan/.gitignore b/cethcan/.gitignore new file mode 100644 index 0000000..35ab3a7 --- /dev/null +++ b/cethcan/.gitignore @@ -0,0 +1,2 @@ +*.o +cethcan diff --git a/cethcan/Makefile b/cethcan/Makefile new file mode 100644 index 0000000..a61d256 --- /dev/null +++ b/cethcan/Makefile @@ -0,0 +1,14 @@ +love: cethcan +.PHONY: love + +PKGS="libevent jansson" + +cethcan: main.o can.o ether.o light.o http.o socketcan.o +	gcc -g -o $@ `pkg-config --libs $(PKGS)` -lcrypto $^ + +clean: +	rm -f *.o *.y.c *.y.h *.l.c cethcan + +%.o: %.c *.h +	gcc -c -g -O0 -Wall -Wextra -Wshadow -pedantic -Wno-unused-parameter -Wno-format -std=gnu99 `pkg-config --cflags $(PKGS)` -o $@ $< + diff --git a/cethcan/can.c b/cethcan/can.c new file mode 100644 index 0000000..5145871 --- /dev/null +++ b/cethcan/can.c @@ -0,0 +1,65 @@ +#include "cethcan.h" + +static struct can_user *users = NULL, **userlast = &users; + +void can_register(struct can_user *user) +{ +	user->next = NULL; +	*userlast = user; +	userlast = &user->next; +} + +struct can_user *can_register_alloc(void *arg, can_handler handler, +	const char *fmt, ...) +{ +	struct can_user *user; +	char *name = NULL; +	va_list ap; + +	va_start(ap, fmt); +	vasprintf(&name, fmt, ap); +	va_end(ap); + +	user = calloc(sizeof(*user), 1); +	user->arg = arg; +	user->handler = handler; +	user->name = name; + +	can_register(user); +	return user; +} + +void can_broadcast(struct can_user *origin, struct can_message *msg) +{ +	struct can_user *u; +	char buf[3*8+1]; + +	msg->origin = origin; + +	if (msg->dlc > 8) { +		lprintf("invalid CAN message (DLC = %zu)", msg->dlc); +		return; +	} +	for (size_t i = 0; i < msg->dlc; i++) +		sprintf(buf + 3 * i, " %02x", msg->bytes[i]); +	lprintf("%s: %08x (%zu)%s", origin->name, +		(unsigned)msg->daddr, msg->dlc, buf); + +	for (u = users; u; u = u->next) +		if (u != origin) +			u->handler(u->arg, msg); +} + +void can_json(json_t *json, enum json_subtype type) +{ +	struct can_user *u; +	for (u = users; u; u = u->next) +		if (u->json) +			u->json(u->arg, json, type); +} + +void can_init(void) +{ +	/* nothing to do */ +} + diff --git a/cethcan/cethcan.h b/cethcan/cethcan.h new file mode 100644 index 0000000..4326429 --- /dev/null +++ b/cethcan/cethcan.h @@ -0,0 +1,85 @@ +#ifndef _CETHCAN_H +#define _CETHCAN_H + +#define _GNU_SOURCE + +#include <stdlib.h> +#include <stdint.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdarg.h> +#include <string.h> +#include <errno.h> +#include <unistd.h> +#include <ctype.h> +#include <sys/time.h> +#include <sys/stat.h> +#include <sys/mman.h> +#include <sys/uio.h> +#include <fcntl.h> +#include <time.h> +#include <assert.h> + +#include <event2/event.h> +#include <jansson.h> + +#include "protocol.h" + +#define lprintf(...) do { \ +	struct timeval tv; struct tm tm; char tvbuf[64]; \ +	gettimeofday(&tv, NULL); localtime_r(&tv.tv_sec, &tm); \ +	strftime(tvbuf, sizeof(tvbuf), "%Y-%m-%d %H:%M:%S", &tm); \ +	fprintf(stderr, "%s.%03d ", tvbuf, tv.tv_usec / 1000); \ +	fprintf(stderr, __VA_ARGS__); \ +	fprintf(stderr, "\n"); \ +	} while (0) + +extern struct event_base *ev_base; + +struct can_user; + +struct can_message { +	struct can_user *origin; + +	uint32_t daddr; + +	uint32_t flags; +#define CAN_MSGF_RTR		(1 << 0) + +	size_t dlc; +	uint8_t bytes[8]; +}; + +enum json_subtype { +	JSON_NORMAL = 0, +	JSON_LONGPOLL = 1, +}; + +typedef void (*can_handler)(void *arg, struct can_message *msg); +typedef void (*json_handler)(void *arg, json_t *json, enum json_subtype type); + +struct can_user { +	struct can_user *next; + +	const char *name; + +	void *arg; +	can_handler handler; +	json_handler json; +}; + +extern void can_register(struct can_user *user); +extern struct can_user *can_register_alloc(void *arg, can_handler handler, +		const char *fmt, ...); +extern void can_broadcast(struct can_user *origin, struct can_message *msg); +extern void can_json(json_t *json, enum json_subtype type); +extern void can_init(void); + +extern void json_bump_longpoll(void); + +extern int socan_init(json_t *config); +extern int ether_init(json_t *config); +extern int light_init_conf(json_t *config); +extern void http_init(void); + +#endif /* _CETHCAN_H */ diff --git a/cethcan/ether.c b/cethcan/ether.c new file mode 100644 index 0000000..5b935ed --- /dev/null +++ b/cethcan/ether.c @@ -0,0 +1,157 @@ +#include "cethcan.h" + +#include <net/if.h> +#include <net/ethernet.h> +#include <netpacket/packet.h> + +#define ETHER_PROTO 0x88b7 +#define ETHER_MCADDR 0xff, 0x3a, 0xf6, 'C', 'A', 'N' + +struct ether { +	struct can_user *u; +	struct event *ev; + +	char *ifname; +	int ifindex; + +	int sock; +}; + +struct frame { +	uint8_t daddr[ETH_ALEN]; +	uint8_t saddr[ETH_ALEN]; +	uint16_t proto; +	uint8_t oui[3]; +	uint16_t protoid; +	uint8_t protover; +	uint32_t ts1, ts2; +	uint32_t can_daddr; +	uint8_t can_dlc; +	uint8_t can_data[8]; +} __attribute__((packed)); + + +static void ether_can_handler(void *arg, struct can_message *msg) +{ +	struct ether *e = arg; +	lprintf("%s: TX not implemented", e->u->name); +} + +static void ether_rx_dataframe(struct ether *e, struct frame *f) +{ +	struct can_message msg; + +	msg.daddr = ntohl(f->can_daddr); +	msg.dlc = f->can_dlc; +	if (msg.dlc > 8) +		msg.dlc = 8; +	memcpy(msg.bytes, f->can_data, msg.dlc); + +	can_broadcast(e->u, &msg); +} + +static void ether_sock_handler(int sock, short event, void *arg) +{ +	struct ether *e = arg; +	union { +		struct sockaddr_ll ll; +		struct sockaddr_storage ss; +		struct sockaddr su; +	} lladdr; +	socklen_t addrlen = sizeof(lladdr); +	union { +		struct frame frame; +		uint8_t raw[1536]; +	} buf; + +	if (event & EV_READ) { +		ssize_t rlen = recvfrom(e->sock, &buf, sizeof(buf), +			MSG_TRUNC, &lladdr.su, &addrlen); +		if (rlen > 0) { +			if (0) { +				lprintf("got %zd bytes", rlen); +				char pbuffer[16 * 3 + 1]; +				for (size_t i = 0; i < (size_t)rlen; i++) { +					size_t j = i % 16; +					sprintf(pbuffer + (3 * j), " %02x", buf.raw[i]); +					if (j == 15) +						lprintf(">>%s", pbuffer); +				} +				if ((rlen % 16) != 15) +					lprintf(">>%s", pbuffer); +			} + +			if (ntohs(buf.frame.proto) == ETHER_PROTO +				&& buf.frame.oui[0] == 0x00 +				&& buf.frame.oui[1] == 0x80 +				&& buf.frame.oui[2] == 0x41 +				&& ntohs(buf.frame.protoid) == 0xaaaa) { +				switch (buf.frame.protover) { +				case 3: +					ether_rx_dataframe(e, &buf.frame); +					break; +				default: +					lprintf("%s: unsupported CAN protocol version %d", +						e->u->name, buf.frame.protover); +				} +			} else { +				lprintf("%s: non-CAN frame (%zd bytes)", +					e->u->name, rlen); +			} +		} +	} +} + +int ether_init(json_t *config) +{ +	struct ether *e; +	const char *iface; +	int ifindex; +	struct packet_mreq pmr = { +		.mr_type = PACKET_MR_MULTICAST, +		.mr_alen = ETH_ALEN, +		.mr_address = { ETHER_MCADDR }, +	}; + +	if (!json_is_object(config)) { +		lprintf("ethernet config must be an object/dictionary"); +		return 1; +	} +	if (!json_is_string(json_object_get(config, "interface"))) { +		lprintf("ethernet config must have an 'interface' key"); +		return 1; +	} +	iface = json_string_value(json_object_get(config, "interface")); +	ifindex = if_nametoindex(iface); + +	if (ifindex == 0) { +		lprintf("ethernet interface '%s' error: %s", +			iface, strerror(errno)); +		return 1; +	} + +	e = calloc(sizeof(*e), 1); +	e->ifname = strdup(iface); +	e->ifindex = ifindex; + +	e->sock = socket(AF_PACKET, SOCK_RAW, htons(ETHER_PROTO)); +	if (e->sock == -1) { +		lprintf("ethernet interface '%s' socket() error: %s", +			iface, strerror(errno)); +		return 1; +	} + +	pmr.mr_ifindex = ifindex; +	if (setsockopt(e->sock, SOL_PACKET, PACKET_ADD_MEMBERSHIP, +		&pmr, sizeof(pmr))) { +		lprintf("ethernet interface '%s' multicast join error: %s", +			iface, strerror(errno)); +		return 1; +	} + +	e->u = can_register_alloc(e, ether_can_handler, "ether[%s]", iface); +	e->ev = event_new(ev_base, e->sock, EV_READ | EV_PERSIST, ether_sock_handler, e); +	event_add(e->ev, NULL); + +	return 0; +} diff --git a/cethcan/http.c b/cethcan/http.c new file mode 100644 index 0000000..8316abc --- /dev/null +++ b/cethcan/http.c @@ -0,0 +1,143 @@ +#include "cethcan.h" + +#include <event2/http.h> +#include <event2/buffer.h> +#include <openssl/sha.h> + +static struct evhttp *evhttp; + +static int evb_json_add(const char *data, size_t size, void *arg) +{ +	struct evbuffer *buf = arg; +	return evbuffer_add(buf, data, size); +} + +static void http_json_basic(struct evhttp_request *req, void *arg) +{ +	struct evkeyvalq *outhdr = evhttp_request_get_output_headers(req); +	struct evbuffer *out = evbuffer_new(); + +	evhttp_add_header(outhdr, "Content-Type", "application/json; charset=utf-8"); + +	json_t *jsout = json_object(); +	can_json(jsout, JSON_NORMAL); +	json_dump_callback(jsout, evb_json_add, out, JSON_SORT_KEYS | JSON_INDENT(4)); +	json_decref(jsout); + +	evhttp_send_reply(req, 200, "OK", out); +	evbuffer_free(out); +} + +static void http_json_bump(struct evhttp_request *req, void *arg) +{ +	struct evkeyvalq *outhdr = evhttp_request_get_output_headers(req); +	struct evbuffer *out = evbuffer_new(); + +	evhttp_add_header(outhdr, "Content-Type", "text/plain; charset=utf-8"); +	evbuffer_add_printf(out, "OK"); +	evhttp_send_reply(req, 200, "OK", out); +	evbuffer_free(out); + +	json_bump_longpoll(); +} + +struct longpoll { +	struct longpoll *next; +	struct evhttp_request *req; +}; +static struct longpoll *longpolls = NULL, **plongpoll = &longpolls; +static size_t longpoll_count = 0; + +static char *longpollbuf = NULL; +static size_t longpollbuflen = 0; +static char longpollhash[SHA_DIGEST_LENGTH * 2 + 1] = ""; + +static void http_json_longpoll(struct evhttp_request *req, void *arg) +{ +	struct evkeyvalq *outhdr = evhttp_request_get_output_headers(req); +	struct evbuffer *out; +	struct longpoll *lp; +	const char *query; +	 +	evhttp_add_header(outhdr, "Content-Type", "application/json; charset=utf-8"); + +	query = evhttp_uri_get_query(evhttp_request_get_evhttp_uri(req)); +	if (query && !strcmp(query, longpollhash)) { +		lprintf("long poll"); +		evhttp_send_reply_start(req, 200, "OK Long Poll"); +		evhttp_request_own(req); + +		lp = calloc(sizeof(*lp), 1); +		lp->req = req; +		*plongpoll = lp; +		plongpoll = &lp->next; +		longpoll_count++; +		return; +	} + +	out = evbuffer_new(); +	evbuffer_add(out, longpollbuf, longpollbuflen); +	evhttp_send_reply(req, 200, "OK Short Poll", out); +	evbuffer_free(out); +} + +static void longpoll_updatedata(void) +{ +	json_t *jsout, *jswrap; +	char *data; +	unsigned char digest[SHA_DIGEST_LENGTH]; + +	if (longpollbuf) +		free(longpollbuf); +	 +	jsout = json_object(); +	can_json(jsout, JSON_LONGPOLL); +	data = json_dumps(jsout, JSON_SORT_KEYS | JSON_INDENT(4)); +	SHA1((unsigned char *)data, strlen(data), digest); +	free(data); + +	for (size_t i = 0; i < SHA_DIGEST_LENGTH; i++) +		sprintf(longpollhash + i * 2, "%02x", digest[i]); + +	jswrap = json_object(); +	json_object_set_new(jswrap, "data", jsout); +	json_object_set_new(jswrap, "ref", json_string(longpollhash)); +	longpollbuf = json_dumps(jswrap, JSON_SORT_KEYS | JSON_INDENT(4)); +	longpollbuflen = strlen(longpollbuf); +	json_decref(jswrap); +} + +void json_bump_longpoll(void) +{ +	struct longpoll *lp, *lpnext; +	struct evbuffer *out; + +	longpoll_updatedata(); + +	out = evbuffer_new(); +	for (lp = longpolls; lp; lp = lpnext) { +		lpnext = lp->next; + +		evbuffer_add(out, longpollbuf, longpollbuflen); +		evhttp_send_reply_chunk(lp->req, out); +		/* send_reply_end calls request_free() */ +		evhttp_send_reply_end(lp->req); +		free(lp); +	} +	evbuffer_free(out); + +	longpolls = NULL; +	plongpoll = &longpolls; +	longpoll_count = 0; +} + +void http_init(void) +{ +	evhttp = evhttp_new(ev_base); +	evhttp_set_cb(evhttp, "/", http_json_basic, NULL); +	evhttp_set_cb(evhttp, "/longpoll", http_json_longpoll, NULL); +	evhttp_set_cb(evhttp, "/bump", http_json_bump, NULL); +	evhttp_bind_socket(evhttp, "127.0.0.1", 34999); + +	longpoll_updatedata(); +} diff --git a/cethcan/light.c b/cethcan/light.c new file mode 100644 index 0000000..cd646d8 --- /dev/null +++ b/cethcan/light.c @@ -0,0 +1,93 @@ +#include "cethcan.h" + +struct value { +	uint8_t val; +	time_t change, valid; +}; + +struct light { +	struct can_user *u; + +	char *name; +	unsigned logical_addr; + +	struct value set, actual; +}; + +static void light_json_handler(void *arg, json_t *json, enum json_subtype type) +{ +	struct light *l = arg; +	json_t *lobj = json_object(); + +	json_object_set_new(lobj, "klass", json_string("light")); +	json_object_set_new(lobj, "addr", json_integer(l->logical_addr)); + +	json_object_set_new(lobj, "actual", json_integer(l->actual.val)); +	if (type != JSON_LONGPOLL) +		json_object_set_new(lobj, "actual_ts", json_integer(l->actual.valid)); +	json_object_set_new(lobj, "actual_tschg", json_integer(l->actual.change)); + +	json_object_set_new(lobj, "set", json_integer(l->set.val)); +	if (type != JSON_LONGPOLL) +		json_object_set_new(lobj, "set_ts", json_integer(l->set.valid)); +	json_object_set_new(lobj, "set_tschg", json_integer(l->set.change)); + +	json_object_set_new(json, l->name, lobj); +} + +static void light_can_handler(void *arg, struct can_message *msg) +{ +	struct light *l = arg; +	struct value *v = NULL; +	unsigned laddr; +	uint8_t dval; + +	if ((msg->daddr & CANA_PROTOCOL) == CANA_LIGHT) +		v = &l->set; +	if ((msg->daddr & CANA_PROTOCOL) == CANA_SENSOR) +		v = &l->actual; +	if (!v) +		return; + +	laddr = msg->daddr & 0xfff; +	if (l->logical_addr < laddr) +		return; +	if (l->logical_addr - laddr >= msg->dlc) +		return; +	dval = msg->bytes[l->logical_addr - laddr]; + +	time(&v->valid); +	if (dval != v->val || v->change == 0) { +		v->val = dval; +		time(&v->change); +		lprintf("%s: set %02x", l->u->name, dval); + +		json_bump_longpoll(); +	} +} + +int light_init_conf(json_t *config) +{ +	struct light *l; + +	if (!json_is_object(config)) { +		lprintf("light config must be an object/dictionary"); +		return 1; +	} +	if (!json_is_integer(json_object_get(config, "addr"))) { +		lprintf("light config must have an 'addr' key"); +		return 1; +	} +	if (!json_is_string(json_object_get(config, "name"))) { +		lprintf("light config must have a 'name' key"); +		return 1; +	} +	 +	l = calloc(sizeof(*l), 1); +	l->name = strdup(json_string_value(json_object_get(config, "name"))); +	l->logical_addr = json_integer_value(json_object_get(config, "addr")); + +	l->u = can_register_alloc(l, light_can_handler, "light[%s]", l->name); +	l->u->json = light_json_handler; +	return 0; +} diff --git a/cethcan/main.c b/cethcan/main.c new file mode 100644 index 0000000..5bdca0a --- /dev/null +++ b/cethcan/main.c @@ -0,0 +1,68 @@ +#include "cethcan.h" + +struct event_base *ev_base; + +int main(int argc, char **argv) +{ +	int optch = 0; +	const char *cfgfile = "cethcan.json"; +	json_error_t je; +	json_t *config, *ethercfg, *lightcfg, *socancfg; + +	do { +		optch = getopt(argc, argv, "c:"); +		switch (optch) { +		case 'c': +			cfgfile = optarg; +			break; +		case -1: +			break; +		} +	} while (optch != -1); + +	if (optind < argc) { +		fprintf(stderr, "leftover arguments\n"); +		return 1; +	} + +	config = json_load_file(cfgfile, JSON_REJECT_DUPLICATES, &je); +	if (!config) { +		fprintf(stderr, "failed to load config:\n%s:%d:%d %s\n", +			je.source, je.line, je.column, je.text); +		return 1; +	} +	if (!json_is_object(config)) { +		fprintf(stderr, "config must be object/dictionary\n"); +		return 1; +	} + +	ev_base = event_base_new(); + +	can_init(); + +	ethercfg = json_object_get(config, "ethernet"); +	for (size_t i = 0; i < json_array_size(ethercfg); i++) { +		json_t *c = json_array_get(ethercfg, i); +		if (ether_init(c)) +			return 1; +	} + +	lightcfg = json_object_get(config, "lights"); +	for (size_t i = 0; i < json_array_size(lightcfg); i++) { +		json_t *c = json_array_get(lightcfg, i); +		if (light_init_conf(c)) +			return 1; +	} + +	socancfg = json_object_get(config, "socketcan"); +	for (size_t i = 0; i < json_array_size(socancfg); i++) { +		json_t *c = json_array_get(socancfg, i); +		if (socan_init(c)) +			return 1; +	} + +	http_init(); + +	event_base_loop(ev_base, 0); +	return 0; +} diff --git a/cethcan/protocol.h b/cethcan/protocol.h new file mode 100644 index 0000000..34327d2 --- /dev/null +++ b/cethcan/protocol.h @@ -0,0 +1,24 @@ +			/* addrs: SSSEEEEE +			 *           8 -> extended addr +			 *          14 -> unused +			 */ +#define CANA_TIME		0x10080000 +#define CANA_DISCOVERY		0x4c080000 +#define CANA_DISCOVERY_F(pg,dst) (CANA_DISCOVERY | (((uint32_t)(pg) & 0xf) << 12) | ((dst) & 0xfff)) +#define CANA_LIGHT		0xcc080000 +#define CANA_LIGHT_F(src,dst)	(CANA_LIGHT | (((src) & 0x3f) << 12) | ((dst) & 0xfff)) +#define CANA_SENSOR		0xe6080000 +#define CANA_SENSOR_F(src)	(CANA_SENSOR | ((src) & 0xfff)) +#define CANA_DEBUG		0xe7000000 + +#define CANA_PROTOCOL		0xffe80000 + +/* +#define can_rx_isext()		(can_rx_addr.b[1] & 0x08) +#define can_rx_ext_rr()		(can_rx_dlc & 0x40) +#define can_rx_len()		(can_rx_dlc & 0x0f) + +#define can_rx_sublab_proto()	((can_rx_addr.b[0] << 8) | (can_rx_addr.b[1] & 0xe8)) +#define can_rx_sublab_addr()	(((can_rx_addr.b[2] & 0x0f) << 8) | can_rx_addr.b[3]) +#define can_rx_sublab_disco_page()	((can_rx_addr.b[2] & 0xf0) >> 4) +*/ diff --git a/cethcan/socketcan.c b/cethcan/socketcan.c new file mode 100644 index 0000000..d73ed45 --- /dev/null +++ b/cethcan/socketcan.c @@ -0,0 +1,134 @@ +#include "cethcan.h" + +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/ioctl.h> + +#include <net/if.h> +#include <linux/can.h> +#include <linux/can/raw.h> + +struct socan { +	struct can_user *u; +	struct event *ev; + +	char *ifname; +	int ifindex; + +	int sock; +}; + +static void socan_handler(void *arg, struct can_message *msg) +{ +	struct socan *sc = arg; +	lprintf("%s: TX not implemented", sc->u->name); +} + +static void socan_event(int sock, short event, void *arg) +{ +	struct socan *sc = arg; +	struct can_message msg; +	union { +		struct sockaddr_can can; +		struct sockaddr_storage ss; +		struct sockaddr su; +	} canaddr; +	socklen_t addrlen = sizeof(canaddr); +	struct canfd_frame frame; + +	if (event & EV_READ) { +		ssize_t rlen = recvfrom(sc->sock, &frame, sizeof(frame), +			MSG_TRUNC, &canaddr.su, &addrlen); +		if (rlen > 0) { +#if 0 +			if (1) { +				lprintf("got %zd bytes", rlen); +				char pbuffer[16 * 3 + 1]; +				for (size_t i = 0; i < (size_t)rlen; i++) { +					size_t j = i % 16; +					sprintf(pbuffer + (3 * j), " %02x", buf.raw[i]); +					if (j == 15) +						lprintf(">>%s", pbuffer); +				} +				if ((rlen % 16) != 15) +					lprintf(">>%s", pbuffer); +			} +#endif +			if (frame.can_id & CAN_EFF_FLAG) +				msg.daddr = 0x00080000 +					| (frame.can_id & 0x0003ffff) +					| ((frame.can_id & 0x1ffc0000) << 3); +			else +				msg.daddr = (frame.can_id & 0x7ff) << 21; +			msg.dlc = frame.len; +			if (msg.dlc > 8) +				msg.dlc = 8; +			memcpy(msg.bytes, frame.data, msg.dlc); +			can_broadcast(sc->u, &msg); +		} +	} +} + +int socan_init(json_t *config) +{ +	struct socan *sc; +	const char *iface; +	int ifindex; +	int on = 1; +	struct sockaddr_can addr; + +	if (!json_is_object(config)) { +		lprintf("socketcan config must be an object/dictionary"); +		return 1; +	} +	if (!json_is_string(json_object_get(config, "interface"))) { +		lprintf("socketcan config must have an 'interface' key"); +		return 1; +	} +	iface = json_string_value(json_object_get(config, "interface")); +	ifindex = if_nametoindex(iface); + +	if (ifindex == 0) { +		lprintf("socketcan interface '%s' error: %s", +			iface, strerror(errno)); +		return 1; +	} + +	sc = calloc(sizeof(*sc), 1); +	sc->ifname = strdup(iface); +	sc->ifindex = ifindex; + +	sc->sock = socket(PF_CAN, SOCK_RAW, CAN_RAW); +	if (sc->sock == -1) { +		lprintf("socketcan interface '%s' socket() error: %s", +			iface, strerror(errno)); +		free(sc); +		return 1; +	} +	if (setsockopt(sc->sock, SOL_CAN_RAW, CAN_RAW_FD_FRAMES, +		&on, sizeof(on))) { +		lprintf("socketcan interface '%s' raw_fd_frames error: %s", +			iface, strerror(errno)); +		return 1; +	} +/*	if (setsockopt(sc->sock, SOL_SOCKET, SO_TIMESTAMP, &on, sizeof(on))) { +		lprintf("socketcan interface '%s' so_timestamp error: %s", +			iface, strerror(errno)); +		return 1; +	} */ + +	addr.can_family = AF_CAN; +	addr.can_ifindex = ifindex; +	if (bind(sc->sock, (struct sockaddr *)&addr, sizeof(addr))) { +		lprintf("socketcan interface '%s' bind error: %s", +			iface, strerror(errno)); +		return 1; +	} + +	sc->u = can_register_alloc(sc, socan_handler, "socketcan[%s]", iface); +	sc->ev = event_new(ev_base, sc->sock, EV_READ | EV_PERSIST, +		socan_event, sc); +	event_add(sc->ev, NULL); + +	return 0; +}  | 
