+++ /dev/null
-/*
- * Linux Interfece for Arexx Data Loggers
- *
- * (c) 2011 Martin Mares <mj@ucw.cz>
- */
-
-#include <stdio.h>
-#include <stdarg.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include <math.h>
-#include <time.h>
-#include <sys/stat.h>
-#include <libusb-1.0/libusb.h>
-#include <rrd.h>
-
-typedef unsigned char byte;
-static libusb_context *usb_ctxt;
-static libusb_device_handle *devh;
-
-static int debug_packets = 0;
-static int debug_raw_data = 0;
-
-#define TIME_OFFSET 946681200 // Timestamp of 2000-01-01 00:00:00
-
-static void die(char *msg, ...)
-{
- va_list args;
- va_start(args, msg);
- vfprintf(stderr, msg, args);
- fputc('\n', stderr);
- va_end(args);
- exit(1);
-}
-
-/*** RRD interface ***/
-
-#define RRD_NAME "/tmp/power.rrd"
-#define SLOT_SIZE 10 // 10 seconds per averaging slot
-#define MAX_ARGS 20
-#define MAX_ARG_SIZE 1024
-
-static int arg_cnt;
-static char *arg_ptr[MAX_ARGS+1];
-static char arg_buf[MAX_ARG_SIZE];
-static int arg_pos;
-
-static void arg_new(void)
-{
- arg_cnt = 1;
- arg_pos = 0;
- arg_ptr[0] = "rrdtool";
-}
-
-static void arg_push(const char *fmt, ...)
-{
- if (arg_cnt >= MAX_ARGS)
- die("MAX_ARGS exceeded");
- va_list va;
- va_start(va, fmt);
- int len = 1 + vsnprintf(arg_buf + arg_pos, MAX_ARG_SIZE - arg_pos, fmt, va);
- if (arg_pos + len > MAX_ARG_SIZE)
- die("MAX_ARG_SIZE exceeded");
- arg_ptr[arg_cnt++] = arg_buf + arg_pos;
- arg_ptr[arg_cnt] = NULL;
- arg_pos += len;
-}
-
-static void rrd_point(time_t t, int id, double val)
-{
- char rr_name[256];
- snprintf(rr_name, sizeof(rr_name), "sensor-%d.rrd", id);
-
- struct stat st;
- if (stat(rr_name, &st) < 0 || !st.st_size) {
- // We have to create the RRD
- printf("### Creating %s\n", rr_name);
- arg_new();
- arg_push(rr_name);
- arg_push("--start");
- arg_push("%d", (int) time(NULL) - 28*86400);
- arg_push("--step");
- arg_push("60");
- arg_push("DS:temp:GAUGE:300:0:10000"); // Anything over 10 kW is considered a ghost
- arg_push("RRA:AVERAGE:0.25:1:20160"); // Last 14 days with full resolution
- arg_push("RRA:AVERAGE:0.25:60:88800"); // Last 10 years with 1h resolution
- rrd_create(arg_cnt, arg_ptr);
- if (rrd_test_error())
- die("rrd_create failed: %s", rrd_get_error());
- }
-
- arg_new();
- arg_push(rr_name);
- arg_push("%d:%f", t, val);
- rrd_update(arg_cnt, arg_ptr);
- if (rrd_test_error())
- printf("### rrd_update failed: %s\n", rrd_get_error());
-}
-
-/*** Transforms ***/
-
-static void cooked_point(time_t t, int id, double val, char *unit, int q)
-{
- struct tm tm;
- localtime_r(&t, &tm);
-
- char tbuf[64];
- strftime(tbuf, sizeof(tbuf), "%Y-%m-%d %H:%M:%S", &tm);
-
- printf("== %s id=%d val=%.3f unit=%s q=%d\n", tbuf, id, val, unit, q);
-
- rrd_point(t, id, val);
-}
-
-static void raw_point(int t, int id, int raw, int q)
-{
- /*
- * The binary blob provided by Arexx contains an embedded XML fragment
- * with descriptions of all known sensor types. If you want to see it,
- * grep the blob for "<deviceinfo>". The meanings of the parameters are
- * as follows:
- *
- * m1, m2 Device type matches if (raw_sensor_id & m1) == m2
- * type Unit measured by the sensor (1=Celsius, 2=RH%, 3=CO2 ppm)
- * dm User-visible sensor ID = raw_sensor_id & dm
- * i 1 if the raw value is signed
- * p[] Coefficients of transformation polynomial (x^0 first)
- * vLo, vUp Upper and lower bound on the final value
- * scale Scaling function:
- * 0 = identity (default)
- * 1 = 10^x
- * 2 = exp(x)
- * 3 = (x < 0) ? 0 : log10(x)
- * 4 = (x < 0) ? 0 : log(x)
- *
- * The raw values are transformed this way:
- * - sign-extend if signed
- * - apply the transformation polynomial
- * - apply the scaling function
- * - drop if outside the interval [vLo,vUp]
- *
- * This function applies the necessary transform for sensors we've
- * seen in the wild. We deliberately ignore the "dm" parameter as we want
- * to report different channels of a single sensor as multiple sensors.
- */
-
- double z = raw;
- double hi, lo;
- char *unit;
- int idhi = id & 0xf000;
-
- if (idhi == 0x1000) {
- z = 0.02*z - 273.15;
- lo = -200;
- hi = 600;
- unit = "C";
- } else if (idhi == 0x2000) {
- if (raw >= 0x8000)
- z -= 0x10000;
- z /= 128;
- lo = -60;
- hi = 125;
- unit = "C";
- } else if (idhi == 0x4000) {
- if (!(id & 1)) {
- z = z/100 - 39.6;
- lo = -60;
- hi = 125;
- unit = "C";
- } else {
- z = -2.8e-6*z*z + 0.0405*z - 4;
- lo = 0;
- hi = 100.1;
- unit = "%RH";
- }
- } else if (idhi == 0x6000) {
- if (!(id & 1)) {
- if (raw >= 0x8000)
- z -= 0x10000;
- z /= 128;
- lo = -60;
- hi = 125;
- unit = "C";
- } else {
- z = -3.8123e-11*z;
- z = (z + 1.9184e-7) * z;
- z = (z - 1.0998e-3) * z;
- z += 6.56;
- z = pow(10, z);
- lo = 0;
- hi = 1e6;
- unit = "ppm";
- }
- } else {
- printf("### Unknown sensor type 0x%04x\n", id);
- return;
- }
-
- if (z < lo || z > hi) {
- printf("### Sensor %d: value %f out of range\n", id, z);
- return;
- }
-
- cooked_point(t + TIME_OFFSET, id, z, unit, q);
-}
-
-/*** USB interface ***/
-
-static void find_device(void)
-{
- libusb_device **devlist;
- ssize_t devn = libusb_get_device_list(usb_ctxt, &devlist);
- if (devn < 0)
- die("Cannot enumerate USB devices: error %d\n", (int) devn);
-
- for (ssize_t i=0; i<devn; i++) {
- struct libusb_device_descriptor desc;
- libusb_device *dev = devlist[i];
- if (!libusb_get_device_descriptor(dev, &desc)) {
- if (desc.idVendor == 0x0451 && desc.idProduct == 0x3211) {
- printf("Arexx USB receiver found at usb%d.%d\n", libusb_get_bus_number(dev), libusb_get_device_address(dev));
- int err;
- if (err = libusb_open(dev, &devh))
- die("libusb_open() failed: error %d\n", err);
- if (err = libusb_claim_interface(devh, 0))
- die("libusb_claim_interface() failed: error %d\n", err);
- libusb_free_device_list(devlist, 1);
- return;
- }
- }
- }
-
- die("No Arexx USB receiver found");
-}
-
-static void dump_packet(byte *pkt)
-{
- for (int i=0; i<64; i++) {
- if (!(i % 16))
- printf("\t%02x:", i);
- printf(" %02x", pkt[i]);
- if (i % 16 == 15)
- printf("\n");
- }
-}
-
-static int send_and_receive(byte *req, byte *reply)
-{
- int err, transferred;
- if (err = libusb_bulk_transfer(devh, 0x01, req, 64, &transferred, 200)) {
- fprintf(stderr, "Transmit error: %d\n", err);
- return 0;
- }
- if (debug_packets) {
- printf(">> xmit %d bytes\n", transferred);
- dump_packet(req);
- }
- if (err = libusb_bulk_transfer(devh, 0x81, reply, 64, &transferred, 200)) {
- fprintf(stderr, "Receive error: %d\n", err);
- return 0;
- }
- if (debug_packets)
- printf("<< recv %d bytes\n", transferred);
- while (transferred < 64)
- reply[transferred++] = 0xff;
- if (debug_packets)
- dump_packet(reply);
- return 1;
-}
-
-static unsigned int get_be16(byte *p)
-{
- return p[1] | (p[0] << 8);
-}
-
-static unsigned int get_le16(byte *p)
-{
- return p[0] | (p[1] << 8);
-}
-
-static unsigned int get_le32(byte *p)
-{
- return get_le16(p) | (get_le16(p+2) << 16);
-}
-
-static void put_le16(byte *p, unsigned int x)
-{
- p[0] = x;
- p[1] = x >> 8;
-}
-
-static void put_le32(byte *p, unsigned int x)
-{
- put_le16(p, x);
- put_le16(p+2, x>>16);
-}
-
-static int parse_packet(byte *reply)
-{
- if (reply[0]) {
- printf("### Unknown packet type %02x\n", reply[0]);
- return 0;
- }
-
- int pos = 1;
- int points = 0;
- while (pos < 64) {
- byte *p = reply + pos;
- int len = p[0];
- if (!len || len == 0xff)
- break;
- if (len < 9 || len > 10) {
- printf("### Unknown tuple length %02x\n", len);
- break;
- }
- if (pos + len > 64) {
- printf("### Tuple truncated\n");
- break;
- }
- int id = get_le16(p+1);
- int raw = get_be16(p+3);
- int t = get_le32(p+5);
- int q = (len > 9) ? p[9] : -1;
- if (debug_raw_data) {
- printf("... %02x: id=%d raw=%d t=%d", len, id, raw, t);
- if (len > 9)
- printf(" q=%d", q);
- printf("\n");
- }
- raw_point(t, id, raw, q);
- pos += len;
- points++;
- }
-
- return points;
-}
-
-static void set_clock(void)
-{
- puts("### Syncing time");
-
- byte req[64], reply[64];
- memset(req, 0, 64);
- req[0] = 4;
- time_t t = time(NULL);
- put_le32(req+1, t-TIME_OFFSET);
- send_and_receive(req, reply);
-
-#if 0
- /*
- * Original software also sends a packet with type 3 and the timestamp,
- * but it does not make any sense, especially as they ignore the sensor
- * readings in the answer.
- */
- req[0] = 3;
- send_and_receive(req, reply);
- parse_packet(reply);
-#endif
-}
-
-int main(void)
-{
- int err;
- if (err = libusb_init(&usb_ctxt))
- die("Cannot initialize libusb: error %d", err);
- libusb_set_debug(usb_ctxt, 3);
-
- find_device();
- set_clock();
-
- for (;;) {
- byte req[64], reply[64];
- memset(req, 0, sizeof(req));
- req[0] = 3;
- if (send_and_receive(req, reply)) {
- if (parse_packet(reply))
- continue;
- }
- sleep(4);
- }
-
- return 0;
-}
--- /dev/null
+/*
+ * Linux Interfece for Arexx Data Loggers
+ *
+ * (c) 2011 Martin Mares <mj@ucw.cz>
+ */
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <math.h>
+#include <time.h>
+#include <getopt.h>
+#include <syslog.h>
+#include <pwd.h>
+#include <grp.h>
+#include <sys/stat.h>
+#include <libusb-1.0/libusb.h>
+#include <rrd.h>
+
+#define LOG_PATH "/var/log/arexxd"
+
+typedef unsigned char byte;
+static libusb_context *usb_ctxt;
+static libusb_device_handle *devh;
+
+static int use_syslog;
+static int debug_mode;
+static int debug_packets;
+static int debug_raw_data;
+
+static void die(char *fmt, ...)
+{
+ va_list args;
+ va_start(args, fmt);
+ if (use_syslog)
+ vsyslog(LOG_CRIT, fmt, args);
+ else {
+ vfprintf(stderr, fmt, args);
+ fprintf(stderr, "\n");
+ }
+ va_end(args);
+ exit(1);
+}
+
+static void log_error(char *fmt, ...)
+{
+ va_list args;
+ va_start(args, fmt);
+ if (use_syslog)
+ vsyslog(LOG_ERR, fmt, args);
+ else {
+ vfprintf(stderr, fmt, args);
+ fprintf(stderr, "\n");
+ }
+ va_end(args);
+}
+
+static void log_info(char *fmt, ...)
+{
+ va_list args;
+ va_start(args, fmt);
+ if (use_syslog)
+ vsyslog(LOG_INFO, fmt, args);
+ else {
+ vfprintf(stderr, fmt, args);
+ fprintf(stderr, "\n");
+ }
+ va_end(args);
+}
+
+static void log_pkt(char *fmt, ...)
+{
+ if (!debug_packets)
+ return;
+ va_list args;
+ va_start(args, fmt);
+ vprintf(fmt, args);
+ va_end(args);
+}
+
+/*** RRD interface ***/
+
+#define SLOT_SIZE 10 // 10 seconds per averaging slot
+#define MAX_ARGS 20
+#define MAX_ARG_SIZE 1024
+
+static int arg_cnt;
+static char *arg_ptr[MAX_ARGS+1];
+static char arg_buf[MAX_ARG_SIZE];
+static int arg_pos;
+
+static void arg_new(void)
+{
+ arg_cnt = 1;
+ arg_pos = 0;
+ arg_ptr[0] = "rrdtool";
+}
+
+static void arg_push(const char *fmt, ...)
+{
+ if (arg_cnt >= MAX_ARGS)
+ die("MAX_ARGS exceeded");
+ va_list va;
+ va_start(va, fmt);
+ int len = 1 + vsnprintf(arg_buf + arg_pos, MAX_ARG_SIZE - arg_pos, fmt, va);
+ if (arg_pos + len > MAX_ARG_SIZE)
+ die("MAX_ARG_SIZE exceeded");
+ arg_ptr[arg_cnt++] = arg_buf + arg_pos;
+ arg_ptr[arg_cnt] = NULL;
+ arg_pos += len;
+}
+
+static void rrd_point(time_t t, int id, double val)
+{
+ char rr_name[256];
+ snprintf(rr_name, sizeof(rr_name), "sensor-%d.rrd", id);
+
+ struct stat st;
+ if (stat(rr_name, &st) < 0 || !st.st_size) {
+ // We have to create the RRD
+ log_info("Creating %s", rr_name);
+ arg_new();
+ arg_push(rr_name);
+ arg_push("--start");
+ arg_push("%d", (int) time(NULL) - 28*86400);
+ arg_push("--step");
+ arg_push("60");
+ arg_push("DS:temp:GAUGE:300:0:10000"); // Anything over 10 kW is considered a ghost
+ arg_push("RRA:AVERAGE:0.25:1:20160"); // Last 14 days with full resolution
+ arg_push("RRA:AVERAGE:0.25:60:88800"); // Last 10 years with 1h resolution
+ rrd_create(arg_cnt, arg_ptr);
+ if (rrd_test_error()) {
+ log_error("rrd_create on %s failed: %s", rr_name, rrd_get_error());
+ return;
+ }
+ }
+
+ arg_new();
+ arg_push(rr_name);
+ arg_push("%d:%f", t, val);
+ rrd_update(arg_cnt, arg_ptr);
+ if (rrd_test_error())
+ log_error("rrd_update on %s failed: %s", rr_name, rrd_get_error());
+}
+
+/*** Transforms ***/
+
+#define TIME_OFFSET 946681200 // Timestamp of 2000-01-01 00:00:00
+
+static void cooked_point(time_t t, int id, double val, char *unit, int q)
+{
+ struct tm tm;
+ localtime_r(&t, &tm);
+
+ char tbuf[64];
+ strftime(tbuf, sizeof(tbuf), "%Y-%m-%d %H:%M:%S", &tm);
+
+ if (debug_raw_data)
+ printf("== %s id=%d val=%.3f unit=%s q=%d\n", tbuf, id, val, unit, q);
+
+ rrd_point(t, id, val);
+}
+
+static void raw_point(int t, int id, int raw, int q)
+{
+ /*
+ * The binary blob provided by Arexx contains an embedded XML fragment
+ * with descriptions of all known sensor types. If you want to see it,
+ * grep the blob for "<deviceinfo>". The meanings of the parameters are
+ * as follows:
+ *
+ * m1, m2 Device type matches if (raw_sensor_id & m1) == m2
+ * type Unit measured by the sensor (1=Celsius, 2=RH%, 3=CO2 ppm)
+ * dm User-visible sensor ID = raw_sensor_id & dm
+ * i 1 if the raw value is signed
+ * p[] Coefficients of transformation polynomial (x^0 first)
+ * vLo, vUp Upper and lower bound on the final value
+ * scale Scaling function:
+ * 0 = identity (default)
+ * 1 = 10^x
+ * 2 = exp(x)
+ * 3 = (x < 0) ? 0 : log10(x)
+ * 4 = (x < 0) ? 0 : log(x)
+ *
+ * The raw values are transformed this way:
+ * - sign-extend if signed
+ * - apply the transformation polynomial
+ * - apply the scaling function
+ * - drop if outside the interval [vLo,vUp]
+ *
+ * This function applies the necessary transform for sensors we've
+ * seen in the wild. We deliberately ignore the "dm" parameter as we want
+ * to report different channels of a single sensor as multiple sensors.
+ */
+
+ double z = raw;
+ double hi, lo;
+ char *unit;
+ int idhi = id & 0xf000;
+
+ if (idhi == 0x1000) {
+ z = 0.02*z - 273.15;
+ lo = -200;
+ hi = 600;
+ unit = "C";
+ } else if (idhi == 0x2000) {
+ if (raw >= 0x8000)
+ z -= 0x10000;
+ z /= 128;
+ lo = -60;
+ hi = 125;
+ unit = "C";
+ } else if (idhi == 0x4000) {
+ if (!(id & 1)) {
+ z = z/100 - 39.6;
+ lo = -60;
+ hi = 125;
+ unit = "C";
+ } else {
+ z = -2.8e-6*z*z + 0.0405*z - 4;
+ lo = 0;
+ hi = 100.1;
+ unit = "%RH";
+ }
+ } else if (idhi == 0x6000) {
+ if (!(id & 1)) {
+ if (raw >= 0x8000)
+ z -= 0x10000;
+ z /= 128;
+ lo = -60;
+ hi = 125;
+ unit = "C";
+ } else {
+ z = -3.8123e-11*z;
+ z = (z + 1.9184e-7) * z;
+ z = (z - 1.0998e-3) * z;
+ z += 6.56;
+ z = pow(10, z);
+ lo = 0;
+ hi = 1e6;
+ unit = "ppm";
+ }
+ } else {
+ log_error("Unknown sensor type 0x%04x", id);
+ return;
+ }
+
+ if (z < lo || z > hi) {
+ log_error("Sensor %d: value %f out of range", id, z);
+ return;
+ }
+
+ cooked_point(t + TIME_OFFSET, id, z, unit, q);
+}
+
+/*** USB interface ***/
+
+static int find_device(void)
+{
+ libusb_device **devlist;
+ ssize_t devn = libusb_get_device_list(usb_ctxt, &devlist);
+ if (devn < 0) {
+ log_error("Cannot enumerate USB devices: error %d", (int) devn);
+ return 0;
+ }
+
+ for (ssize_t i=0; i<devn; i++) {
+ struct libusb_device_descriptor desc;
+ libusb_device *dev = devlist[i];
+ if (!libusb_get_device_descriptor(dev, &desc)) {
+ if (desc.idVendor == 0x0451 && desc.idProduct == 0x3211) {
+ log_info("Arexx USB receiver found at usb%d.%d", libusb_get_bus_number(dev), libusb_get_device_address(dev));
+ int err;
+ if (err = libusb_open(dev, &devh)) {
+ log_error("libusb_open() failed: error %d", err);
+ goto failed;
+ }
+ if (err = libusb_claim_interface(devh, 0)) {
+ log_error("libusb_claim_interface() failed: error %d", err);
+ libusb_close(devh);
+ goto failed;
+ }
+ libusb_free_device_list(devlist, 1);
+ return 1;
+ }
+ }
+ }
+
+failed:
+ libusb_free_device_list(devlist, 1);
+ return 0;
+}
+
+static void release_device(void)
+{
+ libusb_close(devh);
+ devh = NULL;
+}
+
+static void dump_packet(byte *pkt)
+{
+ for (int i=0; i<64; i++) {
+ if (!(i % 16))
+ log_pkt("\t%02x:", i);
+ log_pkt(" %02x", pkt[i]);
+ if (i % 16 == 15)
+ log_pkt("\n");
+ }
+}
+
+static int send_and_receive(byte *req, byte *reply)
+{
+ int err, transferred;
+ if (err = libusb_bulk_transfer(devh, 0x01, req, 64, &transferred, 200)) {
+ if (err == LIBUSB_ERROR_TIMEOUT) {
+ log_pkt(">> xmit timed out\n");
+ return 0;
+ }
+ log_error("Transmit error: %d", err);
+ return err;
+ }
+ if (debug_packets) {
+ log_pkt(">> xmit %d bytes\n", transferred);
+ dump_packet(req);
+ }
+ if (err = libusb_bulk_transfer(devh, 0x81, reply, 64, &transferred, 200)) {
+ if (err == LIBUSB_ERROR_TIMEOUT) {
+ log_pkt(">> recv timed out\n");
+ return 0;
+ }
+ log_error("Receive error: %d", err);
+ return err;
+ }
+ if (debug_packets)
+ log_pkt("<< recv %d bytes\n", transferred);
+ while (transferred < 64)
+ reply[transferred++] = 0xff;
+ if (debug_packets)
+ dump_packet(reply);
+ return 1;
+}
+
+static unsigned int get_be16(byte *p)
+{
+ return p[1] | (p[0] << 8);
+}
+
+static unsigned int get_le16(byte *p)
+{
+ return p[0] | (p[1] << 8);
+}
+
+static unsigned int get_le32(byte *p)
+{
+ return get_le16(p) | (get_le16(p+2) << 16);
+}
+
+static void put_le16(byte *p, unsigned int x)
+{
+ p[0] = x;
+ p[1] = x >> 8;
+}
+
+static void put_le32(byte *p, unsigned int x)
+{
+ put_le16(p, x);
+ put_le16(p+2, x>>16);
+}
+
+static int parse_packet(byte *reply)
+{
+ if (reply[0]) {
+ log_error("Unknown packet type %02x", reply[0]);
+ return 0;
+ }
+
+ int pos = 1;
+ int points = 0;
+ while (pos < 64) {
+ byte *p = reply + pos;
+ int len = p[0];
+ if (!len || len == 0xff)
+ break;
+ if (len < 9 || len > 10) {
+ log_error("Unknown tuple length %02x", len);
+ break;
+ }
+ if (pos + len > 64) {
+ log_error("Tuple truncated");
+ break;
+ }
+ int id = get_le16(p+1);
+ int raw = get_be16(p+3);
+ int t = get_le32(p+5);
+ int q = (len > 9) ? p[9] : -1;
+ if (debug_raw_data) {
+ printf("... %02x: id=%d raw=%d t=%d", len, id, raw, t);
+ if (len > 9)
+ printf(" q=%d", q);
+ printf("\n");
+ }
+ raw_point(t, id, raw, q);
+ pos += len;
+ points++;
+ }
+
+ return points;
+}
+
+static void set_clock(void)
+{
+ log_info("Synchronizing receiver time");
+
+ byte req[64], reply[64];
+ memset(req, 0, 64);
+ req[0] = 4;
+ time_t t = time(NULL);
+ put_le32(req+1, t-TIME_OFFSET);
+ send_and_receive(req, reply);
+
+#if 0
+ /*
+ * Original software also sends a packet with type 3 and the timestamp,
+ * but it does not make any sense, especially as they ignore the sensor
+ * readings in the answer.
+ */
+ req[0] = 3;
+ send_and_receive(req, reply);
+ parse_packet(reply);
+#endif
+}
+
+/*** Main ***/
+
+static const struct option long_options[] = {
+ { "debug", 0, NULL, 'd' },
+ { "log-packets", 0, NULL, 'p' },
+ { NULL, 0, NULL, 0 },
+};
+
+static void usage(void)
+{
+ fprintf(stderr, "Usage: arexxd [--debug] [--log-packets]\n");
+ exit(1);
+}
+
+int main(int argc, char **argv)
+{
+ int opt;
+ while ((opt = getopt_long(argc, argv, "dp", long_options, NULL)) >= 0)
+ switch (opt) {
+ case 'd':
+ debug_mode++;
+ break;
+ case 'p':
+ debug_packets++;
+ debug_raw_data++;
+ break;
+ default:
+ usage();
+ }
+ if (optind < argc)
+ usage();
+
+ int err;
+ if (err = libusb_init(&usb_ctxt))
+ die("Cannot initialize libusb: error %d", err);
+ // libusb_set_debug(usb_ctxt, 3);
+
+ if (!debug_mode) {
+ if (chdir(LOG_PATH) < 0)
+ die("Cannot change directory to %s: %m", LOG_PATH);
+ if (debug_packets || debug_raw_data) {
+ close(1);
+ if (open("debug", O_WRONLY | O_CREAT | O_APPEND, 0666) < 0)
+ die("Cannot open debug log: %m");
+ setlinebuf(stdout);
+ }
+ openlog("arexxd", LOG_NDELAY, LOG_DAEMON);
+ pid_t pid = fork();
+ if (pid < 0)
+ die("fork() failed: %m");
+ if (pid)
+ return 0;
+ setsid();
+ use_syslog = 1;
+ }
+
+ int inited = 0;
+ for (;;) {
+ if (!find_device()) {
+ if (!inited) {
+ inited = 1;
+ log_error("Receiver not connected, waiting until it appears");
+ }
+ sleep(30);
+ continue;
+ }
+
+ inited = 1;
+
+ int sync_in = 0;
+ for (;;) {
+ if (!sync_in) {
+ set_clock();
+ sync_in = 100;
+ }
+ byte req[64], reply[64];
+ memset(req, 0, sizeof(req));
+ req[0] = 3;
+ err = send_and_receive(req, reply);
+ if (err < 0)
+ break;
+ if (err > 0 && parse_packet(reply))
+ continue;
+ sleep(4);
+ sync_in--;
+ }
+
+ log_info("Disconnecting receiver");
+ release_device();
+ }
+
+ return 0;
+}