Browse Source

Merge branch 'master' into hashfast

Conflicts:
	usbutils.h
Con Kolivas 12 years ago
parent
commit
f5baf9f57a
57 changed files with 2194 additions and 3435 deletions
  1. 16 0
      API-README
  2. 1 1
      Makefile.am
  3. 99 1
      NEWS
  4. 402 1
      api.c
  5. 3 14
      autogen.sh
  6. 89 82
      cgminer.c
  7. 1 1
      compat/Makefile.am
  8. 554 0
      compat/jansson-2.5/CHANGES
  9. 1 1
      compat/jansson-2.5/LICENSE
  10. 17 0
      compat/jansson-2.5/Makefile.am
  11. 63 0
      compat/jansson-2.5/README.rst
  12. 54 0
      compat/jansson-2.5/configure.ac
  13. 10 0
      compat/jansson-2.5/jansson.pc.in
  14. 0 0
      compat/jansson-2.5/m4/.gitignore
  15. 24 0
      compat/jansson-2.5/src/Makefile.am
  16. 8 2
      compat/jansson-2.5/src/dump.c
  17. 0 0
      compat/jansson-2.5/src/error.c
  18. 2 2
      compat/jansson-2.5/src/hashtable.c
  19. 1 1
      compat/jansson-2.5/src/hashtable.h
  20. 0 2
      compat/jansson-2.5/src/jansson.def
  21. 20 12
      compat/jansson-2.5/src/jansson.h
  22. 1 1
      compat/jansson-2.5/src/jansson_config.h.in
  23. 2 1
      compat/jansson-2.5/src/jansson_private.h
  24. 28 5
      compat/jansson-2.5/src/load.c
  25. 8 3
      compat/jansson-2.5/src/memory.c
  26. 172 57
      compat/jansson-2.5/src/pack_unpack.c
  27. 8 3
      compat/jansson-2.5/src/strbuffer.c
  28. 3 1
      compat/jansson-2.5/src/strbuffer.h
  29. 5 0
      compat/jansson-2.5/src/strconv.c
  30. 1 1
      compat/jansson-2.5/src/utf.c
  31. 1 1
      compat/jansson-2.5/src/utf.h
  32. 26 13
      compat/jansson-2.5/src/value.c
  33. 0 3
      compat/jansson/.gitignore
  34. 0 21
      compat/jansson/Makefile.am
  35. 0 39
      compat/jansson/jansson_config.h
  36. 0 13
      compat/jansson/util.h
  37. 0 10
      compat/libusb-1.0/doc/Makefile.am
  38. 0 1294
      compat/libusb-1.0/doc/doxygen.cfg.in
  39. 0 15
      compat/libusb-1.0/examples/Makefile.am
  40. 0 507
      compat/libusb-1.0/examples/dpfp.c
  41. 0 545
      compat/libusb-1.0/examples/dpfp_threaded.c
  42. 0 95
      compat/libusb-1.0/examples/hotplugtest.c
  43. 0 64
      compat/libusb-1.0/examples/listdevs.c
  44. 0 193
      compat/libusb-1.0/examples/sam3u_benchmark.c
  45. 0 256
      compat/libusb-1.0/examples/testlibusb1.c
  46. 4 4
      configure.ac
  47. 80 11
      driver-avalon.c
  48. 10 1
      driver-avalon.h
  49. 22 4
      driver-bflsc.c
  50. 1 0
      driver-bflsc.h
  51. 12 8
      driver-icarus.c
  52. 211 71
      driver-klondike.c
  53. 130 51
      miner.h
  54. 61 24
      usbutils.c
  55. 1 0
      usbutils.h
  56. 41 0
      util.c
  57. 1 0
      util.h

+ 16 - 0
API-README

@@ -437,6 +437,12 @@ The list of requests - a (*) means it requires privileged access - and replies:
                                AVA+BTB opt=freq val=256 to 1024 - chip frequency
                                BTB opt=millivolts val=1000 to 1400 - corevoltage
 
+ lockstats     none           There is no reply section just the STATUS section
+                              stating the results of the request
+                              A warning reply means lock stats are not compiled
+                              into cgminer
+                              The API writes all the lock stats to stderr
+
 When you enable, disable or restart a GPU, PGA or ASC, you will also get
 Thread messages in the cgminer status window
 
@@ -491,6 +497,16 @@ miner.php - an example web page to access the API
 Feature Changelog for external applications using the API:
 
 
+API V1.31 (cgminer v3.6.3)
+
+Added API command:
+ 'lockstats' - display cgminer dev lock stats if compiled in
+
+Modified API command:
+ 'summary' - add 'MHS %ds' (where %d is the log interval)
+
+---------
+
 API V1.30 (cgminer v3.4.3)
 
 Added API command:

+ 1 - 1
Makefile.am

@@ -1,7 +1,7 @@
 
 ACLOCAL_AMFLAGS = -I m4
 
-JANSSON_INCLUDES= -I$(top_srcdir)/compat/jansson
+JANSSON_INCLUDES= -I$(top_srcdir)/compat/jansson-2.5/src
 
 if WANT_USBUTILS
 USBUTILS_INCLUDES = -I$(top_srcdir)/compat/libusb-1.0/libusb

+ 99 - 1
NEWS

@@ -1,4 +1,102 @@
-
+Version 3.6.3 - 17th October 2013
+
+- API add 'MHS %ds' to 'summary'
+- Optional lock tracking and stats via the API
+- Speed up polling repeat again in usb poll thread and handle async after the
+message to disable polling is complete.
+- Revert to using timeouts on !linux since libusb leaks memory without them.
+- Revert to libusb instead of libusbx
+
+
+Version 3.6.2 - 17th October 2013
+
+- Remove unused components of jansson
+- Remove unused parts of libusb
+- Work around older libtoolize that fails without top ltmain.sh not being
+present during autogen
+- Fix open coded use of autoreconf in autogen
+- Update jansson to only build parts we require and suited to our build
+environment.
+- Initial import of jansson-2.5
+- Prevent further USB transfers from occurring once the shutdown signal has been
+sent to prevent transfers getting stuck and libusb failing to shut down.
+- Make the USB polling thread poll every second to potentially aid longer
+timeout transfers.
+- Set device_diff on work in get_work to not be missed with drivers that use
+get_work directly.
+- Convert icarus driver to hash_driver_work model.
+- bflsc - also allow ' 0' in DEVICES IN CHAIN
+- bflsc - allow a 0 in DEVICES IN CHAIN
+- Add needed EXTRA_DIST for libusbx.
+- Update libusbx configure.ac changes.
+- Revert libusb Makefile changes from going to libusbx.
+- Fix trivial libusbx warnings.
+- Convert libusb-1.0.16-rc10 to libusbx-1.0.17
+
+
+Version 3.6.1 - 14th October 2013
+
+- Emulate the libusb_control_transfer sync setup in our async variant.
+- usbutils - make all libusb_error_name messages the same
+
+
+Version 3.6.0 - 14th October 2013
+
+- increasing max miners for avalon driver
+- using separate identifier for bitburner fury boards
+- changes to bitburner driver for bitburner fury boards
+- hexstr is too small in test_work_current
+- Windows uses errno for WSAETIMEDOUT
+- Convert the usb callback function to using cgsem_t timed waits to avoid race
+conditions with conditionals/mutexes.
+- Give correct return code in cgsem_mswait
+- Check for correct timeout error in cgsem_mswait
+- Fix util.h exports for cgsem_mswait
+- Implement a generic cgsem_mswait similar to sem_timedwait
+- Use the one LIBUSB_ERROR_TIMEOUT for cancelled transactions since this error
+is explicitly tested for in various drivers.
+- Do not use locking on usb callback function pthread signalling to prevent
+deadlock with libusb's own event lock.
+- Use a write lock when performing any USB control transfers to prevent
+concurrent transfers.
+- Free a libusb transfer after we have finished using it to avoid a dereference
+in usb_control_transfer
+- Do not perform bfi int patching for opencl1.2 or later.
+- Although async transfers are meant to use heap memory, we never return before
+the transfer function has completed so stack memory will suffice for control
+transfers, fixing a memory leak in the process.
+- klondike - correct/reverse min/max stats
+- api incorrect message name
+- klondike - use a link list queue rather than a circular buffer - and add
+timing stats
+- Use a timeout with usb handle events set to a nominal 200ms and wait for the
+polling thread to shut down before deinitialising libusb.
+- Use stack memory for hex used in stratum share submissions.
+- Use stack memory in test_work_current, avoiding a malloc/free cycle each time.
+- Provide a lower level __bin2hex function that does not allocate memory itself.
+- Convert the bitfury driver to use the hash_driver_work version of hash_work.
+- Add a hash_driver_work function to allow for drivers that wish to do their own
+work queueing and management.
+- Convert all usb control transfers to asynchronous communication with our own
+timeout management as well.
+- Klondike - increase circular read buffer size
+- Klondike - extra zero value and range checking in temp conversion
+- klondike - display MHz also
+- Make pthread conditional timeouts handle all bulk usb transfer timeouts
+performing libusb_cancel_transfer, disabling timeouts within libusb itself.
+- Avoid calling get_statline_before on exit to avoid trying to use it on drivers
+in an indeterminate state.
+- Avoid calling get_statline on exit.
+- Add a small amount to the usb timeout before cancelling to allow for a regular
+usb polling interval to pass.
+- Do not attempt to clear a usb halt before sending the cancel message since all
+transfers should normally be cancelled before attempting to clear a halt
+condition, and only change the return message to a timeout if it's consistent
+with a cancellation.
+- Retry up to USB_RETRY_MAX times to clear a halt condition before failing.
+- Show the error number as well as the description in erroring bulk transfers.
+- Drop logging level for failed to connect to stratum to verbose mode only since
+we hit it regularly.
 - We are always dependent on libusb handling events so use the blocking
 libusb_handle_events in the polling thread and use a bool to know if we should
 continue polling.

+ 402 - 1
api.c

@@ -136,7 +136,7 @@ static const char SEPARATOR = '|';
 #define SEPSTR "|"
 static const char GPUSEP = ',';
 
-static const char *APIVERSION = "1.30";
+static const char *APIVERSION = "1.31";
 static const char *DEAD = "Dead";
 #if defined(HAVE_OPENCL) || defined(HAVE_AN_FPGA) || defined(HAVE_AN_ASIC)
 static const char *SICK = "Sick";
@@ -431,6 +431,8 @@ static const char *JSON_PARAMETER = "parameter";
 
 #define MSG_INVNEG 121
 #define MSG_SETQUOTA 122
+#define MSG_LOCKOK 123
+#define MSG_LOCKDIS 124
 
 enum code_severity {
 	SEVERITY_ERR,
@@ -635,6 +637,8 @@ struct CODES {
  { SEVERITY_SUCC,  MSG_ASCSETOK, PARAM_BOTH,	"ASC %d set OK" },
  { SEVERITY_ERR,   MSG_ASCSETERR, PARAM_BOTH,	"ASC %d set failed: %s" },
 #endif
+ { SEVERITY_SUCC,  MSG_LOCKOK,	PARAM_NONE,	"Lock stats created" },
+ { SEVERITY_WARN,  MSG_LOCKDIS,	PARAM_NONE,	"Lock stats not enabled" },
  { SEVERITY_FAIL, 0, 0, NULL }
 };
 
@@ -1432,6 +1436,399 @@ static void message(struct io_data *io_data, int messageid, int paramid, char *p
 		io_add(io_data, JSON_CLOSE);
 }
 
+#if LOCK_TRACKING
+
+#define LOCK_FMT_FFL " - called from %s %s():%d"
+
+#define LOCKMSG(fmt, ...)	fprintf(stderr, "APILOCK: " fmt "\n", ##__VA_ARGS__)
+#define LOCKMSGMORE(fmt, ...)	fprintf(stderr, "          " fmt "\n", ##__VA_ARGS__)
+#define LOCKMSGFFL(fmt, ...) fprintf(stderr, "APILOCK: " fmt LOCK_FMT_FFL "\n", ##__VA_ARGS__, file, func, linenum)
+#define LOCKMSGFLUSH() fflush(stderr)
+
+typedef struct lockstat {
+	uint64_t lock_id;
+	const char *file;
+	const char *func;
+	int linenum;
+	struct timeval tv;
+} LOCKSTAT;
+
+typedef struct lockline {
+	struct lockline *prev;
+	struct lockstat *stat;
+	struct lockline *next;
+} LOCKLINE;
+
+typedef struct lockinfo {
+	void *lock;
+	enum cglock_typ typ;
+	const char *file;
+	const char *func;
+	int linenum;
+	uint64_t gets;
+	uint64_t gots;
+	uint64_t tries;
+	uint64_t dids;
+	uint64_t didnts; // should be tries - dids
+	uint64_t unlocks;
+	LOCKSTAT lastgot;
+	LOCKLINE *lockgets;
+	LOCKLINE *locktries;
+} LOCKINFO;
+
+typedef struct locklist {
+	LOCKINFO *info;
+	struct locklist *next;
+} LOCKLIST;
+
+static uint64_t lock_id = 1;
+
+static LOCKLIST *lockhead;
+
+static void lockmsgnow()
+{
+	struct timeval now;
+	struct tm *tm;
+	time_t dt;
+
+	cgtime(&now);
+
+	dt = now.tv_sec;
+	tm = localtime(&dt);
+
+	LOCKMSG("%d-%02d-%02d %02d:%02d:%02d",
+		tm->tm_year + 1900,
+		tm->tm_mon + 1,
+		tm->tm_mday,
+		tm->tm_hour,
+		tm->tm_min,
+		tm->tm_sec);
+}
+
+static LOCKLIST *newlock(void *lock, enum cglock_typ typ, const char *file, const char *func, const int linenum)
+{
+	LOCKLIST *list;
+
+	list = calloc(1, sizeof(*list));
+	if (!list)
+		quithere(1, "OOM list");
+	list->info = calloc(1, sizeof(*(list->info)));
+	if (!list->info)
+		quithere(1, "OOM info");
+	list->next = lockhead;
+	lockhead = list;
+
+	list->info->lock = lock;
+	list->info->typ = typ;
+	list->info->file = file;
+	list->info->func = func;
+	list->info->linenum = linenum;
+
+	return list;
+}
+
+static LOCKINFO *findlock(void *lock, enum cglock_typ typ, const char *file, const char *func, const int linenum)
+{
+	LOCKLIST *look;
+
+	look = lockhead;
+	while (look) {
+		if (look->info->lock == lock)
+			break;
+		look = look->next;
+	}
+
+	if (!look)
+		look = newlock(lock, typ, file, func, linenum);
+
+	return look->info;
+}
+
+static void addgettry(LOCKINFO *info, uint64_t id, const char *file, const char *func, const int linenum, bool get)
+{
+	LOCKSTAT *stat;
+	LOCKLINE *line;
+
+	stat = calloc(1, sizeof(*stat));
+	if (!stat)
+		quithere(1, "OOM stat");
+	line = calloc(1, sizeof(*line));
+	if (!line)
+		quithere(1, "OOM line");
+
+	if (get)
+		info->gets++;
+	else
+		info->tries++;
+
+	stat->lock_id = id;
+	stat->file = file;
+	stat->func = func;
+	stat->linenum = linenum;
+	cgtime(&stat->tv);
+
+	line->stat = stat;
+
+	if (get) {
+		line->next = info->lockgets;
+		if (info->lockgets)
+			info->lockgets->prev = line;
+		info->lockgets = line;
+	} else {
+		line->next = info->locktries;
+		if (info->locktries)
+			info->locktries->prev = line;
+		info->locktries = line;
+	}
+}
+
+static void markgotdid(LOCKINFO *info, uint64_t id, const char *file, const char *func, const int linenum, bool got, int ret)
+{
+	LOCKLINE *line;
+
+	if (got)
+		info->gots++;
+	else {
+		if (ret == 0)
+			info->dids++;
+		else
+			info->didnts++;
+	}
+
+	if (got || ret == 0) {
+		info->lastgot.lock_id = id;
+		info->lastgot.file = file;
+		info->lastgot.func = func;
+		info->lastgot.linenum = linenum;
+		cgtime(&info->lastgot.tv);
+	}
+
+	if (got)
+		line = info->lockgets;
+	else
+		line = info->locktries;
+	while (line) {
+		if (line->stat->lock_id == id)
+			break;
+		line = line->next;
+	}
+
+	if (!line) {
+		lockmsgnow();
+		LOCKMSGFFL("ERROR attempt to mark a lock as '%s' that wasn't '%s' id=%"PRIu64,
+				got ? "got" : "did/didnt", got ? "get" : "try", id);
+	}
+
+	// Unlink it
+	if (line->prev)
+		line->prev->next = line->next;
+	if (line->next)
+		line->next->prev = line->prev;
+
+	if (got) {
+		if (info->lockgets == line)
+			info->lockgets = line->next;
+	} else {
+		if (info->locktries == line)
+			info->locktries = line->next;
+	}
+
+	free(line->stat);
+	free(line);
+}
+
+// Yes this uses locks also ... ;/
+static void locklock()
+{
+	if (unlikely(pthread_mutex_lock(&lockstat_lock)))
+		quithere(1, "WTF MUTEX ERROR ON LOCK! errno=%d", errno);
+}
+
+static void lockunlock()
+{
+	if (unlikely(pthread_mutex_unlock(&lockstat_lock)))
+		quithere(1, "WTF MUTEX ERROR ON UNLOCK! errno=%d", errno);
+}
+
+uint64_t api_getlock(void *lock, const char *file, const char *func, const int linenum)
+{
+	LOCKINFO *info;
+	uint64_t id;
+
+	locklock();
+
+	info = findlock(lock, CGLOCK_UNKNOWN, file, func, linenum);
+	id = lock_id++;
+	addgettry(info, id, file, func, linenum, true);
+
+	lockunlock();
+
+	return id;
+}
+
+void api_gotlock(uint64_t id, void *lock, const char *file, const char *func, const int linenum)
+{
+	LOCKINFO *info;
+
+	locklock();
+
+	info = findlock(lock, CGLOCK_UNKNOWN, file, func, linenum);
+	markgotdid(info, id, file, func, linenum, true, 0);
+
+	lockunlock();
+}
+
+uint64_t api_trylock(void *lock, const char *file, const char *func, const int linenum)
+{
+	LOCKINFO *info;
+	uint64_t id;
+
+	info = findlock(lock, CGLOCK_UNKNOWN, file, func, linenum);
+	id = lock_id++;
+	addgettry(info, id, file, func, linenum, false);
+
+	return id;
+}
+
+void api_didlock(uint64_t id, int ret, void *lock, const char *file, const char *func, const int linenum)
+{
+	LOCKINFO *info;
+
+	locklock();
+
+	info = findlock(lock, CGLOCK_UNKNOWN, file, func, linenum);
+	markgotdid(info, id, file, func, linenum, false, ret);
+
+	lockunlock();
+}
+
+void api_gunlock(void *lock, const char *file, const char *func, const int linenum)
+{
+	LOCKINFO *info;
+
+	locklock();
+
+	info = findlock(lock, CGLOCK_UNKNOWN, file, func, linenum);
+	info->unlocks++;
+
+	lockunlock();
+}
+
+void api_initlock(void *lock, enum cglock_typ typ, const char *file, const char *func, const int linenum)
+{
+	locklock();
+
+	findlock(lock, typ, file, func, linenum);
+
+	lockunlock();
+}
+
+void dsp_det(char *msg, LOCKSTAT *stat)
+{
+	struct tm *tm;
+	time_t dt;
+
+	dt = stat->tv.tv_sec;
+	tm = localtime(&dt);
+
+	LOCKMSGMORE("%s id=%"PRIu64" by %s %s():%d at %d-%02d-%02d %02d:%02d:%02d",
+			msg,
+			stat->lock_id,
+			stat->file,
+			stat->func,
+			stat->linenum,
+			tm->tm_year + 1900,
+			tm->tm_mon + 1,
+			tm->tm_mday,
+			tm->tm_hour,
+			tm->tm_min,
+			tm->tm_sec);
+}
+
+void dsp_lock(LOCKINFO *info)
+{
+	LOCKLINE *line;
+	char *status;
+
+	LOCKMSG("Lock %p created by %s %s():%d",
+		info->lock,
+		info->file,
+		info->func,
+		info->linenum);
+	LOCKMSGMORE("gets:%"PRIu64" gots:%"PRIu64" tries:%"PRIu64
+		    " dids:%"PRIu64" didnts:%"PRIu64" unlocks:%"PRIu64,
+			info->gets,
+			info->gots,
+			info->tries,
+			info->dids,
+			info->didnts,
+			info->unlocks);
+
+	if (info->gots > 0 || info->dids > 0) {
+		if (info->unlocks < info->gots + info->dids)
+			status = "Last got/did still HELD";
+		else
+			status = "Last got/did (idle)";
+
+		dsp_det(status, &(info->lastgot));
+	} else
+		LOCKMSGMORE("... unused ...");
+
+	if (info->lockgets) {
+		LOCKMSGMORE("BLOCKED gets (%"PRIu64")", info->gets - info->gots);
+		line = info->lockgets;
+		while (line) {
+			dsp_det("", line->stat);
+			line = line->next;
+		}
+	} else
+		LOCKMSGMORE("no blocked gets");
+
+	if (info->locktries) {
+		LOCKMSGMORE("BLOCKED tries (%"PRIu64")", info->tries - info->dids - info->didnts);
+		line = info->lockgets;
+		while (line) {
+			dsp_det("", line->stat);
+			line = line->next;
+		}
+	} else
+		LOCKMSGMORE("no blocked tries");
+}
+
+void show_locks()
+{
+	LOCKLIST *list;
+
+	locklock();
+
+	lockmsgnow();
+
+	list = lockhead;
+	if (!list)
+		LOCKMSG("no locks?!?\n");
+	else {
+		while (list) {
+			dsp_lock(list->info);
+			list = list->next;
+		}
+	}
+
+	LOCKMSGFLUSH();
+
+	lockunlock();
+}
+#endif
+
+static void lockstats(struct io_data *io_data, __maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson, __maybe_unused char group)
+{
+#if LOCK_TRACKING
+	show_locks();
+	message(io_data, MSG_LOCKOK, 0, NULL, isjson);
+#else
+	message(io_data, MSG_LOCKDIS, 0, NULL, isjson);
+#endif
+}
+
 static void apiversion(struct io_data *io_data, __maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson, __maybe_unused char group)
 {
 	struct api_data *root = NULL;
@@ -2177,6 +2574,9 @@ static void summary(struct io_data *io_data, __maybe_unused SOCKETTYPE c, __mayb
 
 	root = api_add_elapsed(root, "Elapsed", &(total_secs), true);
 	root = api_add_mhs(root, "MHS av", &(mhs), false);
+	char mhsname[27];
+	sprintf(mhsname, "MHS %ds", opt_log_interval);
+	root = api_add_mhs(root, mhsname, &(total_rolling), false);
 	root = api_add_uint(root, "Found Blocks", &(found_blocks), true);
 	root = api_add_int(root, "Getworks", &(total_getworks), true);
 	root = api_add_int(root, "Accepted", &(total_accepted), true);
@@ -3878,6 +4278,7 @@ struct CMDS {
 	{ "ascset",		ascset,		true },
 #endif
 	{ "asccount",		asccount,	false },
+	{ "lockstats",		lockstats,	true },
 	{ NULL,			NULL,		false }
 };
 

+ 3 - 14
autogen.sh

@@ -1,20 +1,9 @@
 #!/bin/sh
 bs_dir="$(dirname $(readlink -f $0))"
-rm -rf "${bs_dir}"/autom4te.cache
-rm -f "${bs_dir}"/aclocal.m4 "${bs_dir}"/ltmain.sh
 
-libusb_dir="${bs_dir}"/compat/libusb-1.0/
-rm -rf "${libusb_dir}"/autom4te.cache
-rm -rf "${libusb_dir}"/aclocal.m4 "${libusb_dir}"/ltmain.sh
-
-echo 'Running autoreconf -if...'
-aclocal --force -I m4
-libtoolize --install --copy --force
-autoheader --force
-automake --add-missing --copy --force-missing
-autoconf --force
-
-autoreconf -fi "${libusb_dir}"
+#Some versions of libtoolize don't like there being no ltmain.sh file already
+touch "${bs_dir}"/ltmain.sh
+autoreconf -fi "${bs_dir}"
 
 if test -z "$NOCONFIGURE" ; then
 	echo 'Configuring...'

+ 89 - 82
cgminer.c

@@ -212,6 +212,10 @@ static int new_devices;
 static int new_threads;
 int hotplug_time = 5;
 
+#if LOCK_TRACKING
+pthread_mutex_t lockstat_lock;
+#endif
+
 #ifdef USE_USBUTILS
 pthread_mutex_t cgusb_lock;
 pthread_mutex_t cgusbres_lock;
@@ -237,6 +241,7 @@ pthread_cond_t restart_cond;
 
 pthread_cond_t gws_cond;
 
+double total_rolling;
 double total_mhashes_done;
 static struct timeval total_tv_start, total_tv_end;
 
@@ -1249,7 +1254,10 @@ static struct opt_table opt_config_table[] = {
 		     "Set avalon target temperature"),
 	OPT_WITH_ARG("--bitburner-voltage",
 		     opt_set_intval, NULL, &opt_bitburner_core_voltage,
-		     "Set BitBurner core voltage, in millivolts"),
+		     "Set BitBurner (Avalon) core voltage, in millivolts"),
+	OPT_WITH_ARG("--bitburner-fury-voltage",
+		     opt_set_intval, NULL, &opt_bitburner_fury_core_voltage,
+		     "Set BitBurner Fury core voltage, in millivolts"),
 #endif
 #ifdef USE_KLONDIKE
 	OPT_WITH_ARG("--klondike-options",
@@ -3148,6 +3156,35 @@ static void disable_curses(void)
 }
 #endif
 
+static void kill_timeout(struct thr_info *thr)
+{
+	cg_completion_timeout(&thr_info_cancel, thr, 1000);
+}
+
+static void kill_mining(void)
+{
+	struct thr_info *thr;
+	int i;
+
+	applog(LOG_DEBUG, "Killing off mining threads");
+	/* Kill the mining threads*/
+	for (i = 0; i < mining_threads; i++) {
+		pthread_t *pth = NULL;
+
+		thr = get_thread(i);
+		if (thr && PTH(thr) != 0L)
+			pth = &thr->pth;
+		thr_info_cancel(thr);
+#ifndef WIN32
+		if (pth && *pth)
+			pthread_join(*pth, NULL);
+#else
+		if (pth && pth->p)
+			pthread_join(*pth, NULL);
+#endif
+	}
+}
+
 static void __kill_work(void)
 {
 	struct thr_info *thr;
@@ -3164,19 +3201,19 @@ static void __kill_work(void)
 	if (!opt_scrypt) {
 		applog(LOG_DEBUG, "Killing off HotPlug thread");
 		thr = &control_thr[hotplug_thr_id];
-		thr_info_cancel(thr);
+		kill_timeout(thr);
 	}
 #endif
 
 	applog(LOG_DEBUG, "Killing off watchpool thread");
 	/* Kill the watchpool thread */
 	thr = &control_thr[watchpool_thr_id];
-	thr_info_cancel(thr);
+	kill_timeout(thr);
 
 	applog(LOG_DEBUG, "Killing off watchdog thread");
 	/* Kill the watchdog thread */
 	thr = &control_thr[watchdog_thr_id];
-	thr_info_cancel(thr);
+	kill_timeout(thr);
 
 	applog(LOG_DEBUG, "Shutting down mining threads");
 	for (i = 0; i < mining_threads; i++) {
@@ -3194,43 +3231,27 @@ static void __kill_work(void)
 
 	sleep(1);
 
-	applog(LOG_DEBUG, "Killing off mining threads");
-	/* Kill the mining threads*/
-	for (i = 0; i < mining_threads; i++) {
-		pthread_t *pth = NULL;
-
-		thr = get_thread(i);
-		if (thr && PTH(thr) != 0L)
-			pth = &thr->pth;
-		thr_info_cancel(thr);
-#ifndef WIN32
-		if (pth && *pth)
-			pthread_join(*pth, NULL);
-#else
-		if (pth && pth->p)
-			pthread_join(*pth, NULL);
-#endif
-	}
+	cg_completion_timeout(&kill_mining, NULL, 3000);
 
 	applog(LOG_DEBUG, "Killing off stage thread");
 	/* Stop the others */
 	thr = &control_thr[stage_thr_id];
-	thr_info_cancel(thr);
+	kill_timeout(thr);
 
 	applog(LOG_DEBUG, "Killing off API thread");
 	thr = &control_thr[api_thr_id];
-	thr_info_cancel(thr);
+	kill_timeout(thr);
 
 #ifdef USE_USBUTILS
 	/* Release USB resources in case it's a restart
 	 * and not a QUIT */
 	if (!opt_scrypt) {
 		applog(LOG_DEBUG, "Releasing all USB devices");
-		usb_cleanup();
+		cg_completion_timeout(&usb_cleanup, NULL, 1000);
 
 		applog(LOG_DEBUG, "Killing off usbres thread");
 		thr = &control_thr[usbres_thr_id];
-		thr_info_cancel(thr);
+		kill_timeout(thr);
 	}
 #endif
 
@@ -4505,6 +4526,7 @@ void zero_stats(void)
 	int i;
 
 	cgtime(&total_tv_start);
+	total_rolling = 0;
 	total_mhashes_done = 0;
 	total_getworks = 0;
 	total_accepted = 0;
@@ -5003,7 +5025,6 @@ static void hashmeter(int thr_id, struct timeval *diff,
 	double secs;
 	double local_secs;
 	static double local_mhashes_done = 0;
-	static double rolling = 0;
 	double local_mhashes;
 	bool showlog = false;
 	char displayed_hashes[16], displayed_rolling[16];
@@ -5076,15 +5097,15 @@ static void hashmeter(int thr_id, struct timeval *diff,
 	cgtime(&total_tv_end);
 
 	local_secs = (double)total_diff.tv_sec + ((double)total_diff.tv_usec / 1000000.0);
-	decay_time(&rolling, local_mhashes_done / local_secs, local_secs);
-	global_hashrate = roundl(rolling) * 1000000;
+	decay_time(&total_rolling, local_mhashes_done / local_secs, local_secs);
+	global_hashrate = roundl(total_rolling) * 1000000;
 
 	timersub(&total_tv_end, &total_tv_start, &total_diff);
 	total_secs = (double)total_diff.tv_sec +
 		((double)total_diff.tv_usec / 1000000.0);
 
 	dh64 = (double)total_mhashes_done / total_secs * 1000000ull;
-	dr64 = (double)rolling * 1000000ull;
+	dr64 = (double)total_rolling * 1000000ull;
 	suffix_string(dh64, displayed_hashes, sizeof(displayed_hashes), 4);
 	suffix_string(dr64, displayed_rolling, sizeof(displayed_rolling), 4);
 
@@ -5992,6 +6013,7 @@ struct work *get_work(struct thr_info *thr, const int thr_id)
 	work->thr_id = thr_id;
 	thread_reportin(thr);
 	work->mined = true;
+	work->device_diff = MIN(thr->cgpu->drv->max_diff, work->work_difficulty);
 	return work;
 }
 
@@ -6326,56 +6348,39 @@ static void hash_sole_work(struct thr_info *mythr)
 	cgpu->deven = DEV_DISABLED;
 }
 
-/* Create a hashtable of work items for devices with a queue. The device
- * driver must have a custom queue_full function or it will default to true
- * and put only one work item in the queue. Work items should not be removed
- * from this hashtable until they are no longer in use anywhere. Once a work
- * item is physically queued on the device itself, the work->queued flag
- * should be set under cgpu->qlock write lock to prevent it being dereferenced
- * while still in use. */
+/* Put a new unqueued work item in cgpu->unqueued_work under cgpu->qlock till
+ * the driver tells us it's full so that it may extract the work item using
+ * the get_queued() function which adds it to the hashtable on
+ * cgpu->queued_work. */
 static void fill_queue(struct thr_info *mythr, struct cgpu_info *cgpu, struct device_drv *drv, const int thr_id)
 {
 	do {
-		bool need_work;
-
-		rd_lock(&cgpu->qlock);
-		need_work = (HASH_COUNT(cgpu->queued_work) == cgpu->queued_count);
-		rd_unlock(&cgpu->qlock);
-
-		if (need_work) {
-			struct work *work = get_work(mythr, thr_id);
-
-			work->device_diff = MIN(drv->max_diff, work->work_difficulty);
-			wr_lock(&cgpu->qlock);
-			HASH_ADD_INT(cgpu->queued_work, id, work);
-			wr_unlock(&cgpu->qlock);
-		}
+		wr_lock(&cgpu->qlock);
+		if (!cgpu->unqueued_work)
+			cgpu->unqueued_work = get_work(mythr, thr_id);
+		wr_unlock(&cgpu->qlock);
 		/* The queue_full function should be used by the driver to
 		 * actually place work items on the physical device if it
 		 * does have a queue. */
 	} while (!drv->queue_full(cgpu));
 }
 
-/* This function is for retrieving one work item from the queued hashtable of
- * available work items that are not yet physically on a device (which is
- * flagged with the work->queued bool). Code using this function must be able
- * to handle NULL as a return which implies there is no work available. */
+/* This function is for retrieving one work item from the unqueued pointer and
+ * adding it to the hashtable of queued work. Code using this function must be
+ * able to handle NULL as a return which implies there is no work available. */
 struct work *get_queued(struct cgpu_info *cgpu)
 {
-	struct work *work, *tmp, *ret = NULL;
+	struct work *work = NULL;
 
 	wr_lock(&cgpu->qlock);
-	HASH_ITER(hh, cgpu->queued_work, work, tmp) {
-		if (!work->queued) {
-			work->queued = true;
-			cgpu->queued_count++;
-			ret = work;
-			break;
-		}
+	if (cgpu->unqueued_work) {
+		work = cgpu->unqueued_work;
+		HASH_ADD_INT(cgpu->queued_work, id, work);
+		cgpu->unqueued_work = NULL;
 	}
 	wr_unlock(&cgpu->qlock);
 
-	return ret;
+	return work;
 }
 
 /* This function is for finding an already queued work item in the
@@ -6388,8 +6393,7 @@ struct work *__find_work_bymidstate(struct work *que, char *midstate, size_t mid
 	struct work *work, *tmp, *ret = NULL;
 
 	HASH_ITER(hh, que, work, tmp) {
-		if (work->queued &&
-		    memcmp(work->midstate, midstate, midstatelen) == 0 &&
+		if (memcmp(work->midstate, midstate, midstatelen) == 0 &&
 		    memcmp(work->data + offset, data, datalen) == 0) {
 			ret = work;
 			break;
@@ -6427,10 +6431,9 @@ struct work *clone_queued_work_bymidstate(struct cgpu_info *cgpu, char *midstate
 	return ret;
 }
 
-static void __work_completed(struct cgpu_info *cgpu, struct work *work)
+void __work_completed(struct cgpu_info *cgpu, struct work *work)
 {
-	if (work->queued)
-		cgpu->queued_count--;
+	cgpu->queued_count--;
 	HASH_DEL(cgpu->queued_work, work);
 }
 /* This function should be used by queued device drivers when they're sure
@@ -6461,23 +6464,17 @@ struct work *take_queued_work_bymidstate(struct cgpu_info *cgpu, char *midstate,
 
 static void flush_queue(struct cgpu_info *cgpu)
 {
-	struct work *work, *tmp;
-	int discarded = 0;
+	struct work *work = NULL;
 
 	wr_lock(&cgpu->qlock);
-	HASH_ITER(hh, cgpu->queued_work, work, tmp) {
-		/* Can only discard the work items if they're not physically
-		 * queued on the device. */
-		if (!work->queued) {
-			HASH_DEL(cgpu->queued_work, work);
-			discard_work(work);
-			discarded++;
-		}
-	}
+	work = cgpu->unqueued_work;
+	cgpu->unqueued_work = NULL;
 	wr_unlock(&cgpu->qlock);
 
-	if (discarded)
-		applog(LOG_DEBUG, "Discarded %d queued work items", discarded);
+	if (work) {
+		free_work(work);
+		applog(LOG_DEBUG, "Discarded queued work item");
+	}
 }
 
 /* This version of hash work is for devices that are fast enough to always
@@ -7799,12 +7796,16 @@ static void probe_pools(void)
 #ifdef USE_USBUTILS
 static void *libusb_poll_thread(void __maybe_unused *arg)
 {
-	struct timeval tv_end = {0, 200000};
+	struct timeval tv_end = {0, 100000};
 
 	RenameThread("usbpoll");
 
 	while (usb_polling)
 		libusb_handle_events_timeout_completed(NULL, &tv_end, NULL);
+	/* One longer poll on shut down to enable drivers to hopefully cleanly
+	 * shut down. */
+	tv_end.tv_sec = 1;
+	libusb_handle_events_timeout_completed(NULL, &tv_end, NULL);
 
 	return NULL;
 }
@@ -7840,6 +7841,12 @@ int main(int argc, char *argv[])
 	if (unlikely(curl_global_init(CURL_GLOBAL_ALL)))
 		quit(1, "Failed to curl_global_init");
 
+#if LOCK_TRACKING
+	// Must be first
+	if (unlikely(pthread_mutex_init(&lockstat_lock, NULL)))
+		quithere(1, "Failed to pthread_mutex_init lockstat_lock errno=%d", errno);
+#endif
+
 	initial_args = malloc(sizeof(char *) * (argc + 1));
 	for  (i = 0; i < argc; i++)
 		initial_args[i] = strdup(argv[i]);

+ 1 - 1
compat/Makefile.am

@@ -1,5 +1,5 @@
 
-SUBDIRS	= jansson
+SUBDIRS	= jansson-2.5
 
 if WANT_USBUTILS
 SUBDIRS += libusb-1.0

+ 554 - 0
compat/jansson-2.5/CHANGES

@@ -0,0 +1,554 @@
+Version 2.5
+===========
+
+Released 2013-09-19
+
+* New features:
+
+  - `json_pack()` and friends: Add format specifiers ``s#``, ``+`` and
+    ``+#``.
+
+  - Add ``JSON_DECODE_INT_AS_REAL`` decoding flag to treat all numbers
+    as real in the decoder (#123).
+
+  - Add `json_array_foreach()`, paralleling `json_object_foreach()`
+    (#118).
+
+* Bug fixes:
+
+  - `json_dumps()` and friends: Don't crash if json is *NULL* and
+    ``JSON_ENCODE_ANY`` is set.
+
+  - Fix a theoretical integer overflow in `jsonp_strdup()`.
+
+  - Fix `l_isxdigit()` macro (#97).
+
+  - Fix an off-by-one error in `json_array_remove()`.
+
+* Build:
+
+  - Support CMake in addition to GNU Autotools (#106, #107, #112,
+    #115, #120, #127).
+
+  - Support building for Android (#109).
+
+  - Don't use ``-Werror`` by default.
+
+  - Support building and testing with VPATH (#93).
+
+  - Fix compilation when ``NDEBUG`` is defined (#128)
+
+* Tests:
+
+  - Fix a refleak in ``test/bin/json_process.c``.
+
+* Documentation:
+
+  - Clarify the return value of `json_load_callback_t`.
+
+  - Document how to circumvent problems with separate heaps on Windows.
+
+  - Fix memory leaks and warnings in ``github_commits.c``.
+
+  - Use `json_decref()` properly in tutorial.
+
+* Other:
+
+  - Make it possible to forward declare ``struct json_t``.
+
+
+Version 2.4
+===========
+
+Released 2012-09-23
+
+* New features:
+
+  - Add `json_boolean()` macro that returns the JSON true or false
+    value based on its argument (#86).
+
+  - Add `json_load_callback()` that calls a callback function
+    repeatedly to read the JSON input (#57).
+
+  - Add JSON_ESCAPE_SLASH encoding flag to escape all occurences of
+    ``/`` with ``\/``.
+
+* Bug fixes:
+
+  - Check for and reject NaN and Inf values for reals. Encoding these
+    values resulted in invalid JSON.
+
+  - Fix `json_real_set()` to return -1 on error.
+
+* Build:
+
+  - Jansson now builds on Windows with Visual Studio 2010, and
+    includes solution and project files in ``win32/vs2010/``
+    directory.
+
+  - Fix build warnings (#77, #78).
+
+  - Add ``-no-undefined`` to LDFLAGS (#90).
+
+* Tests:
+
+  - Fix the symbol exports test on Linux/PPC64 (#88).
+
+* Documentation:
+
+  - Fix typos (#73, #84).
+
+
+Version 2.3.1
+=============
+
+Released 2012-04-20
+
+* Build issues:
+
+  - Only use ``long long`` if ``strtoll()`` is also available.
+
+* Documentation:
+
+  - Fix the names of library version constants in documentation. (#52)
+
+  - Change the tutorial to use GitHub API v3. (#65)
+
+* Tests:
+
+  - Make some tests locale independent. (#51)
+
+  - Distribute the library exports test in the tarball.
+
+  - Make test run on shells that don't support the ``export FOO=bar``
+    syntax.
+
+
+Version 2.3
+===========
+
+Released 2012-01-27
+
+* New features:
+
+  - `json_unpack()` and friends: Add support for optional object keys
+    with the ``{s?o}`` syntax.
+
+  - Add `json_object_update_existing()` and
+    `json_object_update_missing()`, for updating only existing keys or
+    only adding missing keys to an object. (#37)
+
+  - Add `json_object_foreach()` for more convenient iteration over
+    objects. (#45, #46)
+
+  - When decoding JSON, write the number of bytes that were read from
+    input to ``error.position`` also on success. This is handy with
+    ``JSON_DISABLE_EOF_CHECK``.
+
+  - Add support for decoding any JSON value, not just arrays or
+    objects. The support is enabled with the new ``JSON_DECODE_ANY``
+    flag. Patch by Andrea Marchesini. (#4)
+
+* Bug fixes
+
+  - Avoid problems with object's serial number growing too big. (#40,
+    #41)
+
+  - Decoding functions now return NULL if the first argument is NULL.
+    Patch by Andrea Marchesini.
+
+  - Include ``jansson_config.h.win32`` in the distribution tarball.
+
+  - Remove ``+`` and leading zeros from exponents in the encoder.
+    (#39)
+
+  - Make Jansson build and work on MinGW. (#39, #38)
+
+* Documentation
+
+  - Note that the same JSON values must not be encoded in parallel by
+    separate threads. (#42)
+
+  - Document MinGW support.
+
+
+Version 2.2.1
+=============
+
+Released 2011-10-06
+
+* Bug fixes:
+
+  - Fix real number encoding and decoding under non-C locales. (#32)
+
+  - Fix identifier decoding under non-UTF-8 locales. (#35)
+
+  - `json_load_file()`: Open the input file in binary mode for maximum
+    compatiblity.
+
+* Documentation:
+
+  - Clarify the lifecycle of the result of the ``s`` fromat of
+    `json_unpack()`. (#31)
+
+  - Add some portability info. (#36)
+
+  - Little clarifications here and there.
+
+* Other:
+
+  - Some style fixes, issues detected by static analyzers.
+
+
+Version 2.2
+===========
+
+Released 2011-09-03
+
+* New features:
+
+  - `json_dump_callback()`: Pass the encoder output to a callback
+    function in chunks.
+
+* Bug fixes:
+
+  - `json_string_set()`: Check that target is a string and value is
+    not NULL.
+
+* Other:
+
+  - Documentation typo fixes and clarifications.
+
+
+Version 2.1
+===========
+
+Released 2011-06-10
+
+* New features:
+
+  - `json_loadb()`: Decode a string with a given size, useful if the
+    string is not null terminated.
+
+  - Add ``JSON_ENCODE_ANY`` encoding flag to allow encoding any JSON
+    value. By default, only arrays and objects can be encoded. (#19)
+
+  - Add ``JSON_REJECT_DUPLICATES`` decoding flag to issue a decoding
+    error if any JSON object in the input contins duplicate keys. (#3)
+
+  - Add ``JSON_DISABLE_EOF_CHECK`` decoding flag to stop decoding after a
+    valid JSON input. This allows other data after the JSON data.
+
+* Bug fixes:
+
+  - Fix an additional memory leak when memory allocation fails in
+    `json_object_set()` and friends.
+
+  - Clear errno before calling `strtod()` for better portability. (#27)
+
+* Building:
+
+  - Avoid set-but-not-used warning/error in a test. (#20)
+
+* Other:
+
+  - Minor clarifications to documentation.
+
+
+Version 2.0.1
+=============
+
+Released 2011-03-31
+
+* Bug fixes:
+
+  - Replace a few `malloc()` and `free()` calls with their
+    counterparts that support custom memory management.
+
+  - Fix object key hashing in json_unpack() strict checking mode.
+
+  - Fix the parentheses in ``JANSSON_VERSION_HEX`` macro.
+
+  - Fix `json_object_size()` return value.
+
+  - Fix a few compilation issues.
+
+* Portability:
+
+  - Enhance portability of `va_copy()`.
+
+  - Test framework portability enhancements.
+
+* Documentation:
+
+  - Distribute ``doc/upgrading.rst`` with the source tarball.
+
+  - Build documentation in strict mode in ``make distcheck``.
+
+
+Version 2.0
+===========
+
+Released 2011-02-28
+
+This release is backwards incompatible with the 1.x release series.
+See the chapter "Upgrading from older versions" in documentation for
+details.
+
+* Backwards incompatible changes:
+
+  - Unify unsigned integer usage in the API: All occurences of
+    unsigned int and unsigned long have been replaced with size_t.
+
+  - Change JSON integer's underlying type to the widest signed integer
+    type available, i.e. long long if it's supported, otherwise long.
+    Add a typedef json_int_t that defines the type.
+
+  - Change the maximum indentation depth to 31 spaces in encoder. This
+    frees up bits from the flags parameter of encoding functions
+    `json_dumpf()`, `json_dumps()` and `json_dump_file()`.
+
+  - For future needs, add a flags parameter to all decoding functions
+    `json_loadf()`, `json_loads()` and `json_load_file()`.
+
+* New features
+
+  - `json_pack()`, `json_pack_ex()`, `json_vpack_ex()`: Create JSON
+    values based on a format string.
+
+  - `json_unpack()`, `json_unpack_ex()`, `json_vunpack_ex()`: Simple
+    value extraction and validation functionality based on a format
+    string.
+
+  - Add column, position and source fields to the ``json_error_t``
+    struct.
+
+  - Enhance error reporting in the decoder.
+
+  - ``JANSSON_VERSION`` et al.: Preprocessor constants that define the
+    library version.
+
+  - `json_set_alloc_funcs()`: Set custom memory allocation functions.
+
+* Fix many portability issues, especially on Windows.
+
+* Configuration
+
+  - Add file ``jansson_config.h`` that contains site specific
+    configuration. It's created automatically by the configure script,
+    or can be created by hand if the configure script cannot be used.
+    The file ``jansson_config.h.win32`` can be used without
+    modifications on Windows systems.
+
+  - Add a section to documentation describing how to build Jansson on
+    Windows.
+
+  - Documentation now requires Sphinx 1.0 or newer.
+
+
+Version 1.3
+===========
+
+Released 2010-06-13
+
+* New functions:
+
+  - `json_object_iter_set()`, `json_object_iter_set_new()`: Change
+    object contents while iterating over it.
+
+  - `json_object_iter_at()`: Return an iterator that points to a
+    specific object item.
+
+* New encoding flags:
+
+  - ``JSON_PRESERVE_ORDER``: Preserve the insertion order of object
+    keys.
+
+* Bug fixes:
+
+  - Fix an error that occured when an array or object was first
+    encoded as empty, then populated with some data, and then
+    re-encoded
+
+  - Fix the situation like above, but when the first encoding resulted
+    in an error
+
+* Documentation:
+
+  - Clarify the documentation on reference stealing, providing an
+    example usage pattern
+
+
+Version 1.2.1
+=============
+
+Released 2010-04-03
+
+* Bug fixes:
+
+  - Fix reference counting on ``true``, ``false`` and ``null``
+  - Estimate real number underflows in decoder with 0.0 instead of
+    issuing an error
+
+* Portability:
+
+  - Make ``int32_t`` available on all systems
+  - Support compilers that don't have the ``inline`` keyword
+  - Require Autoconf 2.60 (for ``int32_t``)
+
+* Tests:
+
+  - Print test names correctly when ``VERBOSE=1``
+  - ``test/suites/api``: Fail when a test fails
+  - Enhance tests for iterators
+  - Enhance tests for decoding texts that contain null bytes
+
+* Documentation:
+
+  - Don't remove ``changes.rst`` in ``make clean``
+  - Add a chapter on RFC conformance
+
+
+Version 1.2
+===========
+
+Released 2010-01-21
+
+* New functions:
+
+  - `json_equal()`: Test whether two JSON values are equal
+  - `json_copy()` and `json_deep_copy()`: Make shallow and deep copies
+    of JSON values
+  - Add a version of all functions taking a string argument that
+    doesn't check for valid UTF-8: `json_string_nocheck()`,
+    `json_string_set_nocheck()`, `json_object_set_nocheck()`,
+    `json_object_set_new_nocheck()`
+
+* New encoding flags:
+
+  - ``JSON_SORT_KEYS``: Sort objects by key
+  - ``JSON_ENSURE_ASCII``: Escape all non-ASCII Unicode characters
+  - ``JSON_COMPACT``: Use a compact representation with all unneeded
+    whitespace stripped
+
+* Bug fixes:
+
+  - Revise and unify whitespace usage in encoder: Add spaces between
+    array and object items, never append newline to output.
+  - Remove const qualifier from the ``json_t`` parameter in
+    `json_string_set()`, `json_integer_set()` and `json_real_set`.
+  - Use ``int32_t`` internally for representing Unicode code points
+    (int is not enough on all platforms)
+
+* Other changes:
+
+  - Convert ``CHANGES`` (this file) to reStructured text and add it to
+    HTML documentation
+  - The test system has been refactored. Python is no longer required
+    to run the tests.
+  - Documentation can now be built by invoking ``make html``
+  - Support for pkg-config
+
+
+Version 1.1.3
+=============
+
+Released 2009-12-18
+
+* Encode reals correctly, so that first encoding and then decoding a
+  real always produces the same value
+* Don't export private symbols in ``libjansson.so``
+
+
+Version 1.1.2
+=============
+
+Released 2009-11-08
+
+* Fix a bug where an error message was not produced if the input file
+  could not be opened in `json_load_file()`
+* Fix an assertion failure in decoder caused by a minus sign without a
+  digit after it
+* Remove an unneeded include of ``stdint.h`` in ``jansson.h``
+
+
+Version 1.1.1
+=============
+
+Released 2009-10-26
+
+* All documentation files were not distributed with v1.1; build
+  documentation in make distcheck to prevent this in the future
+* Fix v1.1 release date in ``CHANGES``
+
+
+Version 1.1
+===========
+
+Released 2009-10-20
+
+* API additions and improvements:
+
+  - Extend array and object APIs
+  - Add functions to modify integer, real and string values
+  - Improve argument validation
+  - Use unsigned int instead of ``uint32_t`` for encoding flags
+
+* Enhance documentation
+
+  - Add getting started guide and tutorial
+  - Fix some typos
+  - General clarifications and cleanup
+
+* Check for integer and real overflows and underflows in decoder
+* Make singleton values thread-safe (``true``, ``false`` and ``null``)
+* Enhance circular reference handling
+* Don't define ``-std=c99`` in ``AM_CFLAGS``
+* Add C++ guards to ``jansson.h``
+* Minor performance and portability improvements
+* Expand test coverage
+
+
+Version 1.0.4
+=============
+
+Released 2009-10-11
+
+* Relax Autoconf version requirement to 2.59
+* Make Jansson compile on platforms where plain ``char`` is unsigned
+* Fix API tests for object
+
+
+Version 1.0.3
+=============
+
+Released 2009-09-14
+
+* Check for integer and real overflows and underflows in decoder
+* Use the Python json module for tests, or simplejson if the json
+  module is not found
+* Distribute changelog (this file)
+
+
+Version 1.0.2
+=============
+
+Released 2009-09-08
+
+* Handle EOF correctly in decoder
+
+
+Version 1.0.1
+=============
+
+Released 2009-09-04
+
+* Fixed broken `json_is_boolean()`
+
+
+Version 1.0
+===========
+
+Released 2009-08-25
+
+* Initial release

+ 1 - 1
compat/jansson/LICENSE → compat/jansson-2.5/LICENSE

@@ -1,4 +1,4 @@
-Copyright (c) 2009, 2010 Petri Lehtinen <petri@digip.org>
+Copyright (c) 2009-2013 Petri Lehtinen <petri@digip.org>
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal

+ 17 - 0
compat/jansson-2.5/Makefile.am

@@ -0,0 +1,17 @@
+ACLOCAL_AMFLAGS = -I m4
+
+EXTRA_DIST = CHANGES LICENSE README.rst
+SUBDIRS = src
+
+# "make distcheck" builds the dvi target, so use it to check that the
+# documentation is built correctly.
+dvi:
+	$(MAKE) SPHINXOPTS_EXTRA=-W html
+
+pkgconfigdir = $(libdir)/pkgconfig
+pkgconfig_DATA = jansson.pc
+
+if GCC
+# These flags are gcc specific
+export AM_CFLAGS = -Wall -Wextra -Wdeclaration-after-statement
+endif

+ 63 - 0
compat/jansson-2.5/README.rst

@@ -0,0 +1,63 @@
+Jansson README
+==============
+
+.. image:: https://travis-ci.org/akheron/jansson.png
+  :alt: Build status
+  :target: https://travis-ci.org/akheron/jansson
+
+Jansson_ is a C library for encoding, decoding and manipulating JSON
+data. Its main features and design principles are:
+
+- Simple and intuitive API and data model
+
+- Comprehensive documentation
+
+- No dependencies on other libraries
+
+- Full Unicode support (UTF-8)
+
+- Extensive test suite
+
+Jansson is licensed under the `MIT license`_; see LICENSE in the
+source distribution for details.
+
+
+Compilation and Installation
+----------------------------
+
+If you obtained a source tarball, just use the standard autotools
+commands::
+
+   $ ./configure
+   $ make
+   $ make install
+
+To run the test suite, invoke::
+
+   $ make check
+
+If the source has been checked out from a Git repository, the
+./configure script has to be generated first. The easiest way is to
+use autoreconf::
+
+   $ autoreconf -i
+
+
+Documentation
+-------------
+
+Prebuilt HTML documentation is available at
+http://www.digip.org/jansson/doc/.
+
+The documentation source is in the ``doc/`` subdirectory. To generate
+HTML documentation, invoke::
+
+   $ make html
+
+Then, point your browser to ``doc/_build/html/index.html``. Sphinx_
+1.0 or newer is required to generate the documentation.
+
+
+.. _Jansson: http://www.digip.org/jansson/
+.. _`MIT license`: http://www.opensource.org/licenses/mit-license.php
+.. _Sphinx: http://sphinx.pocoo.org/

+ 54 - 0
compat/jansson-2.5/configure.ac

@@ -0,0 +1,54 @@
+AC_PREREQ([2.60])
+AC_INIT([jansson], [2.5], [petri@digip.org])
+
+AC_CONFIG_MACRO_DIR([m4])
+
+AM_INIT_AUTOMAKE([1.10 foreign])
+
+AC_CONFIG_SRCDIR([src/value.c])
+AC_CONFIG_HEADERS([config.h])
+
+# Checks for programs.
+AC_PROG_CC
+AC_PROG_LIBTOOL
+AM_CONDITIONAL([GCC], [test x$GCC = xyes])
+
+# Checks for libraries.
+
+# Checks for header files.
+AC_CHECK_HEADERS([locale.h])
+
+# Checks for typedefs, structures, and compiler characteristics.
+AC_TYPE_INT32_T
+AC_TYPE_LONG_LONG_INT
+
+AC_C_INLINE
+case $ac_cv_c_inline in
+    yes) json_inline=inline;;
+    no) json_inline=;;
+    *) json_inline=$ac_cv_c_inline;;
+esac
+AC_SUBST([json_inline])
+
+# Checks for library functions.
+AC_CHECK_FUNCS([strtoll localeconv])
+
+case "$ac_cv_type_long_long_int$ac_cv_func_strtoll" in
+     yesyes) json_have_long_long=1;;
+     *) json_have_long_long=0;;
+esac
+AC_SUBST([json_have_long_long])
+
+case "$ac_cv_header_locale_h$ac_cv_func_localeconv" in
+     yesyes) json_have_localeconv=1;;
+     *) json_have_localeconv=0;;
+esac
+AC_SUBST([json_have_localeconv])
+
+AC_CONFIG_FILES([
+        jansson.pc
+        Makefile
+        src/Makefile
+        src/jansson_config.h
+])
+AC_OUTPUT

+ 10 - 0
compat/jansson-2.5/jansson.pc.in

@@ -0,0 +1,10 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=${prefix}/include
+
+Name: Jansson
+Description: Library for encoding, decoding and manipulating JSON data
+Version: @VERSION@
+Libs: -L${libdir} -ljansson
+Cflags: -I${includedir}

+ 0 - 0
compat/jansson-2.5/m4/.gitignore


+ 24 - 0
compat/jansson-2.5/src/Makefile.am

@@ -0,0 +1,24 @@
+EXTRA_DIST = jansson.def
+
+include_HEADERS = jansson.h jansson_config.h
+
+lib_LTLIBRARIES = libjansson.la
+libjansson_la_SOURCES = \
+	dump.c \
+	error.c \
+	hashtable.c \
+	hashtable.h \
+	jansson_private.h \
+	load.c \
+	memory.c \
+	pack_unpack.c \
+	strbuffer.c \
+	strbuffer.h \
+	strconv.c \
+	utf.c \
+	utf.h \
+	value.c
+libjansson_la_LDFLAGS = \
+	-no-undefined \
+	-export-symbols-regex '^json_' \
+	-version-info 9:0:5

+ 8 - 2
compat/jansson/dump.c → compat/jansson-2.5/src/dump.c

@@ -1,11 +1,14 @@
 /*
- * Copyright (c) 2009-2012 Petri Lehtinen <petri@digip.org>
+ * Copyright (c) 2009-2013 Petri Lehtinen <petri@digip.org>
  *
  * Jansson is free software; you can redistribute it and/or modify
  * it under the terms of the MIT license. See LICENSE for details.
  */
 
+#ifndef _GNU_SOURCE
 #define _GNU_SOURCE
+#endif
+
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -38,7 +41,7 @@ static int dump_to_file(const char *buffer, size_t size, void *data)
 }
 
 /* 32 spaces (the maximum indentation size) */
-static char whitespace[] = "                                ";
+static const char whitespace[] = "                                ";
 
 static int dump_indent(size_t flags, int depth, int space, json_dump_callback_t dump, void *data)
 {
@@ -171,6 +174,9 @@ static int object_key_compare_serials(const void *key1, const void *key2)
 static int do_dump(const json_t *json, size_t flags, int depth,
                    json_dump_callback_t dump, void *data)
 {
+    if(!json)
+        return -1;
+
     switch(json_typeof(json)) {
         case JSON_NULL:
             return dump("null", 4, data);

+ 0 - 0
compat/jansson/error.c → compat/jansson-2.5/src/error.c


+ 2 - 2
compat/jansson/hashtable.c → compat/jansson-2.5/src/hashtable.c

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009-2012 Petri Lehtinen <petri@digip.org>
+ * Copyright (c) 2009-2013 Petri Lehtinen <petri@digip.org>
  *
  * This library is free software; you can redistribute it and/or modify
  * it under the terms of the MIT license. See LICENSE for details.
@@ -74,7 +74,7 @@ static void insert_to_bucket(hashtable_t *hashtable, bucket_t *bucket,
     }
 }
 
-static size_t primes[] = {
+static const size_t primes[] = {
     5, 13, 23, 53, 97, 193, 389, 769, 1543, 3079, 6151, 12289, 24593,
     49157, 98317, 196613, 393241, 786433, 1572869, 3145739, 6291469,
     12582917, 25165843, 50331653, 100663319, 201326611, 402653189,

+ 1 - 1
compat/jansson/hashtable.h → compat/jansson-2.5/src/hashtable.h

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009-2012 Petri Lehtinen <petri@digip.org>
+ * Copyright (c) 2009-2013 Petri Lehtinen <petri@digip.org>
  *
  * This library is free software; you can redistribute it and/or modify
  * it under the terms of the MIT license. See LICENSE for details.

+ 0 - 2
compat/jansson/jansson.def → compat/jansson-2.5/src/jansson.def

@@ -1,5 +1,3 @@
-LIBRARY "jansson"
-
 EXPORTS
     json_delete
     json_true

+ 20 - 12
compat/jansson/jansson.h → compat/jansson-2.5/src/jansson.h

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009-2012 Petri Lehtinen <petri@digip.org>
+ * Copyright (c) 2009-2013 Petri Lehtinen <petri@digip.org>
  *
  * Jansson is free software; you can redistribute it and/or modify
  * it under the terms of the MIT license. See LICENSE for details.
@@ -21,11 +21,11 @@ extern "C" {
 /* version */
 
 #define JANSSON_MAJOR_VERSION  2
-#define JANSSON_MINOR_VERSION  4
+#define JANSSON_MINOR_VERSION  5
 #define JANSSON_MICRO_VERSION  0
 
 /* Micro version is omitted if it's 0 */
-#define JANSSON_VERSION  "2.4"
+#define JANSSON_VERSION  "2.5"
 
 /* Version as a 3-byte hex number, e.g. 0x010201 == 1.2.1. Use this
    for numeric comparisons, e.g. #if JANSSON_VERSION_HEX >= ... */
@@ -47,11 +47,12 @@ typedef enum {
     JSON_NULL
 } json_type;
 
-typedef struct {
+typedef struct json_t {
     json_type type;
     size_t refcount;
 } json_t;
 
+#ifndef JANSSON_USING_CMAKE /* disabled if using cmake */
 #if JSON_INTEGER_IS_LONG_LONG
 #ifdef _WIN32
 #define JSON_INTEGER_FORMAT "I64d"
@@ -63,6 +64,7 @@ typedef long long json_int_t;
 #define JSON_INTEGER_FORMAT "ld"
 typedef long json_int_t;
 #endif /* JSON_INTEGER_IS_LONG_LONG */
+#endif
 
 #define json_typeof(json)      ((json)->type)
 #define json_is_object(json)   (json && json_typeof(json) == JSON_OBJECT)
@@ -146,6 +148,11 @@ int json_object_iter_set_new(json_t *object, void *iter, json_t *value);
         key && (value = json_object_iter_value(json_object_key_to_iter(key))); \
         key = json_object_iter_key(json_object_iter_next(object, json_object_key_to_iter(key))))
 
+#define json_array_foreach(array, index, value) \
+	for(index = 0; \
+		index < json_array_size(array) && (value = json_array_get(array, index)); \
+		index++)
+
 static JSON_INLINE
 int json_object_set(json_t *object, const char *key, json_t *value)
 {
@@ -174,9 +181,9 @@ int json_array_clear(json_t *array);
 int json_array_extend(json_t *array, json_t *other);
 
 static JSON_INLINE
-int json_array_set(json_t *array, size_t index, json_t *value)
+int json_array_set(json_t *array, size_t ind, json_t *value)
 {
-    return json_array_set_new(array, index, json_incref(value));
+    return json_array_set_new(array, ind, json_incref(value));
 }
 
 static JSON_INLINE
@@ -186,9 +193,9 @@ int json_array_append(json_t *array, json_t *value)
 }
 
 static JSON_INLINE
-int json_array_insert(json_t *array, size_t index, json_t *value)
+int json_array_insert(json_t *array, size_t ind, json_t *value)
 {
-    return json_array_insert_new(array, index, json_incref(value));
+    return json_array_insert_new(array, ind, json_incref(value));
 }
 
 const char *json_string_value(const json_t *string);
@@ -224,14 +231,15 @@ int json_equal(json_t *value1, json_t *value2);
 /* copying */
 
 json_t *json_copy(json_t *value);
-json_t *json_deep_copy(json_t *value);
+json_t *json_deep_copy(const json_t *value);
 
 
 /* decoding */
 
-#define JSON_REJECT_DUPLICATES 0x1
-#define JSON_DISABLE_EOF_CHECK 0x2
-#define JSON_DECODE_ANY        0x4
+#define JSON_REJECT_DUPLICATES  0x1
+#define JSON_DISABLE_EOF_CHECK  0x2
+#define JSON_DECODE_ANY         0x4
+#define JSON_DECODE_INT_AS_REAL 0x8
 
 typedef size_t (*json_load_callback_t)(void *buffer, size_t buflen, void *data);
 

+ 1 - 1
compat/jansson/jansson_config.h.in → compat/jansson-2.5/src/jansson_config.h.in

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010-2012 Petri Lehtinen <petri@digip.org>
+ * Copyright (c) 2010-2013 Petri Lehtinen <petri@digip.org>
  *
  * Jansson is free software; you can redistribute it and/or modify
  * it under the terms of the MIT license. See LICENSE for details.

+ 2 - 1
compat/jansson/jansson_private.h → compat/jansson-2.5/src/jansson_private.h

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009-2012 Petri Lehtinen <petri@digip.org>
+ * Copyright (c) 2009-2013 Petri Lehtinen <petri@digip.org>
  *
  * Jansson is free software; you can redistribute it and/or modify
  * it under the terms of the MIT license. See LICENSE for details.
@@ -81,6 +81,7 @@ int jsonp_dtostr(char *buffer, size_t size, double value);
 /* Wrappers for custom memory functions */
 void* jsonp_malloc(size_t size);
 void jsonp_free(void *ptr);
+char *jsonp_strndup(const char *str, size_t length);
 char *jsonp_strdup(const char *str);
 
 /* Windows compatibility */

+ 28 - 5
compat/jansson/load.c → compat/jansson-2.5/src/load.c

@@ -1,11 +1,14 @@
 /*
- * Copyright (c) 2009-2012 Petri Lehtinen <petri@digip.org>
+ * Copyright (c) 2009-2013 Petri Lehtinen <petri@digip.org>
  *
  * Jansson is free software; you can redistribute it and/or modify
  * it under the terms of the MIT license. See LICENSE for details.
  */
 
+#ifndef _GNU_SOURCE
 #define _GNU_SOURCE
+#endif
+
 #include <errno.h>
 #include <limits.h>
 #include <stdio.h>
@@ -37,7 +40,7 @@
 #define l_isalpha(c)  (l_isupper(c) || l_islower(c))
 #define l_isdigit(c)  ('0' <= (c) && (c) <= '9')
 #define l_isxdigit(c) \
-    (l_isdigit(c) || 'A' <= (c) || (c) <= 'F' || 'a' <= (c) || (c) <= 'f')
+    (l_isdigit(c) || ('A' <= (c) && (c) <= 'F') || ('a' <= (c) && (c) <= 'f'))
 
 /* Read one byte from stream, convert to unsigned char, then int, and
    return. return EOF on end of file. This corresponds to the
@@ -250,9 +253,18 @@ static void lex_unget(lex_t *lex, int c)
 static void lex_unget_unsave(lex_t *lex, int c)
 {
     if(c != STREAM_STATE_EOF && c != STREAM_STATE_ERROR) {
+        /* Since we treat warnings as errors, when assertions are turned
+         * off the "d" variable would be set but never used. Which is
+         * treated as an error by GCC.
+         */
+        #ifndef NDEBUG
         char d;
+        #endif
         stream_unget(&lex->stream, c);
-        d = strbuffer_pop(&lex->saved_text);
+        #ifndef NDEBUG
+        d = 
+        #endif
+            strbuffer_pop(&lex->saved_text);
         assert(c == d);
     }
 }
@@ -446,8 +458,9 @@ out:
     jsonp_free(lex->value.string);
 }
 
+#ifndef JANSSON_USING_CMAKE /* disabled if using cmake */
 #if JSON_INTEGER_IS_LONG_LONG
-#ifdef _MSC_VER // Microsoft Visual Studio
+#ifdef _MSC_VER  /* Microsoft Visual Studio */
 #define json_strtoint     _strtoi64
 #else
 #define json_strtoint     strtoll
@@ -455,6 +468,7 @@ out:
 #else
 #define json_strtoint     strtol
 #endif
+#endif
 
 static int lex_scan_number(lex_t *lex, int c, json_error_t *error)
 {
@@ -770,6 +784,7 @@ error:
 static json_t *parse_value(lex_t *lex, size_t flags, json_error_t *error)
 {
     json_t *json;
+    double value;
 
     switch(lex->token) {
         case TOKEN_STRING: {
@@ -778,7 +793,15 @@ static json_t *parse_value(lex_t *lex, size_t flags, json_error_t *error)
         }
 
         case TOKEN_INTEGER: {
-            json = json_integer(lex->value.integer);
+            if (flags & JSON_DECODE_INT_AS_REAL) {
+                if(jsonp_strtod(&lex->saved_text, &value)) {
+                    error_set(error, lex, "real number overflow");
+                    return NULL;
+                }
+                json = json_real(value);
+            } else {
+                json = json_integer(lex->value.integer);
+            }
             break;
         }
 

+ 8 - 3
compat/jansson/memory.c → compat/jansson-2.5/src/memory.c

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009-2012 Petri Lehtinen <petri@digip.org>
+ * Copyright (c) 2009-2013 Petri Lehtinen <petri@digip.org>
  * Copyright (c) 2011-2012 Basile Starynkevitch <basile@starynkevitch.net>
  *
  * Jansson is free software; you can redistribute it and/or modify it
@@ -35,12 +35,17 @@ void jsonp_free(void *ptr)
 char *jsonp_strdup(const char *str)
 {
     char *new_str;
+    size_t len;
 
-    new_str = jsonp_malloc(strlen(str) + 1);
+    len = strlen(str);
+    if(len == (size_t)-1)
+        return NULL;
+
+    new_str = jsonp_malloc(len + 1);
     if(!new_str)
         return NULL;
 
-    strcpy(new_str, str);
+    memcpy(new_str, str, len + 1);
     return new_str;
 }
 

+ 172 - 57
compat/jansson/pack_unpack.c → compat/jansson-2.5/src/pack_unpack.c

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009-2012 Petri Lehtinen <petri@digip.org>
+ * Copyright (c) 2009-2013 Petri Lehtinen <petri@digip.org>
  * Copyright (c) 2011-2012 Graeme Smecher <graeme.smecher@mail.mcgill.ca>
  *
  * Jansson is free software; you can redistribute it and/or modify
@@ -11,17 +11,29 @@
 #include "jansson_private.h"
 #include "utf.h"
 
+typedef struct {
+    int line;
+    int column;
+    size_t pos;
+    char token;
+} token_t;
+
 typedef struct {
     const char *start;
     const char *fmt;
-    char token;
+    token_t prev_token;
+    token_t token;
+    token_t next_token;
     json_error_t *error;
     size_t flags;
     int line;
     int column;
+    size_t pos;
 } scanner_t;
 
-static const char *type_names[] = {
+#define token(scanner) ((scanner)->token.token)
+
+static const char * const type_names[] = {
     "object",
     "array",
     "string",
@@ -34,7 +46,7 @@ static const char *type_names[] = {
 
 #define type_name(x) type_names[json_typeof(x)]
 
-static const char *unpack_value_starters = "{[siIbfFOon";
+static const char unpack_value_starters[] = "{[siIbfFOon";
 
 
 static void scanner_init(scanner_t *s, json_error_t *error,
@@ -43,14 +55,28 @@ static void scanner_init(scanner_t *s, json_error_t *error,
     s->error = error;
     s->flags = flags;
     s->fmt = s->start = fmt;
+    memset(&s->prev_token, 0, sizeof(token_t));
+    memset(&s->token, 0, sizeof(token_t));
+    memset(&s->next_token, 0, sizeof(token_t));
     s->line = 1;
     s->column = 0;
+    s->pos = 0;
 }
 
 static void next_token(scanner_t *s)
 {
-    const char *t = s->fmt;
+    const char *t;
+    s->prev_token = s->token;
+
+    if(s->next_token.line) {
+        s->token = s->next_token;
+        s->next_token.line = 0;
+        return;
+    }
+
+    t = s->fmt;
     s->column++;
+    s->pos++;
 
     /* skip space and ignored chars */
     while(*t == ' ' || *t == '\t' || *t == '\n' || *t == ',' || *t == ':') {
@@ -61,23 +87,32 @@ static void next_token(scanner_t *s)
         else
             s->column++;
 
+        s->pos++;
         t++;
     }
 
-    s->token = *t;
+    s->token.token = *t;
+    s->token.line = s->line;
+    s->token.column = s->column;
+    s->token.pos = s->pos;
 
     t++;
     s->fmt = t;
 }
 
+static void prev_token(scanner_t *s)
+{
+    s->next_token = s->token;
+    s->token = s->prev_token;
+}
+
 static void set_error(scanner_t *s, const char *source, const char *fmt, ...)
 {
     va_list ap;
-    size_t pos;
     va_start(ap, fmt);
 
-    pos = (size_t)(s->fmt - s->start);
-    jsonp_error_vset(s->error, s->line, s->column, pos, fmt, ap);
+    jsonp_error_vset(s->error, s->token.line, s->token.column, s->token.pos,
+                     fmt, ap);
 
     jsonp_error_set_source(s->error, source);
 
@@ -86,35 +121,107 @@ static void set_error(scanner_t *s, const char *source, const char *fmt, ...)
 
 static json_t *pack(scanner_t *s, va_list *ap);
 
+
+/* ours will be set to 1 if jsonp_free() must be called for the result
+   afterwards */
+static char *read_string(scanner_t *s, va_list *ap,
+                         const char *purpose, int *ours)
+{
+    char t;
+    strbuffer_t strbuff;
+    const char *str;
+    size_t length;
+    char *result;
+
+    next_token(s);
+    t = token(s);
+    prev_token(s);
+
+    if(t != '#' && t != '+') {
+        /* Optimize the simple case */
+        str = va_arg(*ap, const char *);
+
+        if(!str) {
+            set_error(s, "<args>", "NULL string argument");
+            return NULL;
+        }
+
+        if(!utf8_check_string(str, -1)) {
+            set_error(s, "<args>", "Invalid UTF-8 %s", purpose);
+            return NULL;
+        }
+
+        *ours = 0;
+        return (char *)str;
+    }
+
+    strbuffer_init(&strbuff);
+
+    while(1) {
+        str = va_arg(*ap, const char *);
+        if(!str) {
+            set_error(s, "<args>", "NULL string argument");
+            strbuffer_close(&strbuff);
+            return NULL;
+        }
+
+        next_token(s);
+
+        if(token(s) == '#') {
+            length = va_arg(*ap, int);
+        }
+        else {
+            prev_token(s);
+            length = strlen(str);
+        }
+
+        if(strbuffer_append_bytes(&strbuff, str, length) == -1) {
+            set_error(s, "<internal>", "Out of memory");
+            strbuffer_close(&strbuff);
+            return NULL;
+        }
+
+        next_token(s);
+        if(token(s) != '+') {
+            prev_token(s);
+            break;
+        }
+    }
+
+    result = strbuffer_steal_value(&strbuff);
+
+    if(!utf8_check_string(result, -1)) {
+        set_error(s, "<args>", "Invalid UTF-8 %s", purpose);
+        return NULL;
+    }
+
+    *ours = 1;
+    return result;
+}
+
 static json_t *pack_object(scanner_t *s, va_list *ap)
 {
     json_t *object = json_object();
     next_token(s);
 
-    while(s->token != '}') {
-        const char *key;
+    while(token(s) != '}') {
+        char *key;
+        int ours;
         json_t *value;
 
-        if(!s->token) {
+        if(!token(s)) {
             set_error(s, "<format>", "Unexpected end of format string");
             goto error;
         }
 
-        if(s->token != 's') {
-            set_error(s, "<format>", "Expected format 's', got '%c'", s->token);
+        if(token(s) != 's') {
+            set_error(s, "<format>", "Expected format 's', got '%c'", token(s));
             goto error;
         }
 
-        key = va_arg(*ap, const char *);
-        if(!key) {
-            set_error(s, "<args>", "NULL object key");
+        key = read_string(s, ap, "object key", &ours);
+        if(!key)
             goto error;
-        }
-
-        if(!utf8_check_string(key, -1)) {
-            set_error(s, "<args>", "Invalid UTF-8 in object key");
-            goto error;
-        }
 
         next_token(s);
 
@@ -123,10 +230,16 @@ static json_t *pack_object(scanner_t *s, va_list *ap)
             goto error;
 
         if(json_object_set_new_nocheck(object, key, value)) {
+            if(ours)
+                jsonp_free(key);
+
             set_error(s, "<internal>", "Unable to add key \"%s\"", key);
             goto error;
         }
 
+        if(ours)
+            jsonp_free(key);
+
         next_token(s);
     }
 
@@ -142,10 +255,10 @@ static json_t *pack_array(scanner_t *s, va_list *ap)
     json_t *array = json_array();
     next_token(s);
 
-    while(s->token != ']') {
+    while(token(s) != ']') {
         json_t *value;
 
-        if(!s->token) {
+        if(!token(s)) {
             set_error(s, "<format>", "Unexpected end of format string");
             goto error;
         }
@@ -170,25 +283,27 @@ error:
 
 static json_t *pack(scanner_t *s, va_list *ap)
 {
-    switch(s->token) {
+    switch(token(s)) {
         case '{':
             return pack_object(s, ap);
 
         case '[':
             return pack_array(s, ap);
 
-        case 's': /* string */
-        {
-            const char *str = va_arg(*ap, const char *);
-            if(!str) {
-                set_error(s, "<args>", "NULL string argument");
-                return NULL;
-            }
-            if(!utf8_check_string(str, -1)) {
-                set_error(s, "<args>", "Invalid UTF-8 string");
+        case 's': { /* string */
+            char *str;
+            int ours;
+            json_t *result;
+
+            str = read_string(s, ap, "string", &ours);
+            if(!str)
                 return NULL;
-            }
-            return json_string_nocheck(str);
+
+            result = json_string_nocheck(str);
+            if(ours)
+                jsonp_free(str);
+
+            return result;
         }
 
         case 'n': /* null */
@@ -214,7 +329,7 @@ static json_t *pack(scanner_t *s, va_list *ap)
 
         default:
             set_error(s, "<format>", "Unexpected format character '%c'",
-                      s->token);
+                      token(s));
             return NULL;
     }
 }
@@ -245,30 +360,30 @@ static int unpack_object(scanner_t *s, json_t *root, va_list *ap)
     }
     next_token(s);
 
-    while(s->token != '}') {
+    while(token(s) != '}') {
         const char *key;
         json_t *value;
         int opt = 0;
 
         if(strict != 0) {
             set_error(s, "<format>", "Expected '}' after '%c', got '%c'",
-                      (strict == 1 ? '!' : '*'), s->token);
+                      (strict == 1 ? '!' : '*'), token(s));
             goto out;
         }
 
-        if(!s->token) {
+        if(!token(s)) {
             set_error(s, "<format>", "Unexpected end of format string");
             goto out;
         }
 
-        if(s->token == '!' || s->token == '*') {
-            strict = (s->token == '!' ? 1 : -1);
+        if(token(s) == '!' || token(s) == '*') {
+            strict = (token(s) == '!' ? 1 : -1);
             next_token(s);
             continue;
         }
 
-        if(s->token != 's') {
-            set_error(s, "<format>", "Expected format 's', got '%c'", s->token);
+        if(token(s) != 's') {
+            set_error(s, "<format>", "Expected format 's', got '%c'", token(s));
             goto out;
         }
 
@@ -280,7 +395,7 @@ static int unpack_object(scanner_t *s, json_t *root, va_list *ap)
 
         next_token(s);
 
-        if(s->token == '?') {
+        if(token(s) == '?') {
             opt = 1;
             next_token(s);
         }
@@ -331,30 +446,30 @@ static int unpack_array(scanner_t *s, json_t *root, va_list *ap)
     }
     next_token(s);
 
-    while(s->token != ']') {
+    while(token(s) != ']') {
         json_t *value;
 
         if(strict != 0) {
             set_error(s, "<format>", "Expected ']' after '%c', got '%c'",
                       (strict == 1 ? '!' : '*'),
-                      s->token);
+                      token(s));
             return -1;
         }
 
-        if(!s->token) {
+        if(!token(s)) {
             set_error(s, "<format>", "Unexpected end of format string");
             return -1;
         }
 
-        if(s->token == '!' || s->token == '*') {
-            strict = (s->token == '!' ? 1 : -1);
+        if(token(s) == '!' || token(s) == '*') {
+            strict = (token(s) == '!' ? 1 : -1);
             next_token(s);
             continue;
         }
 
-        if(!strchr(unpack_value_starters, s->token)) {
+        if(!strchr(unpack_value_starters, token(s))) {
             set_error(s, "<format>", "Unexpected format character '%c'",
-                      s->token);
+                      token(s));
             return -1;
         }
 
@@ -392,7 +507,7 @@ static int unpack_array(scanner_t *s, json_t *root, va_list *ap)
 
 static int unpack(scanner_t *s, json_t *root, va_list *ap)
 {
-    switch(s->token)
+    switch(token(s))
     {
         case '{':
             return unpack_object(s, root, ap);
@@ -521,7 +636,7 @@ static int unpack(scanner_t *s, json_t *root, va_list *ap)
 
         default:
             set_error(s, "<format>", "Unexpected format character '%c'",
-                      s->token);
+                      token(s));
             return -1;
     }
 }
@@ -551,7 +666,7 @@ json_t *json_vpack_ex(json_error_t *error, size_t flags,
         return NULL;
 
     next_token(&s);
-    if(s.token) {
+    if(token(&s)) {
         json_decref(value);
         set_error(&s, "<format>", "Garbage after format string");
         return NULL;
@@ -614,7 +729,7 @@ int json_vunpack_ex(json_t *root, json_error_t *error, size_t flags,
     va_end(ap_copy);
 
     next_token(&s);
-    if(s.token) {
+    if(token(&s)) {
         set_error(&s, "<format>", "Garbage after format string");
         return -1;
     }

+ 8 - 3
compat/jansson/strbuffer.c → compat/jansson-2.5/src/strbuffer.c

@@ -1,11 +1,14 @@
 /*
- * Copyright (c) 2009-2012 Petri Lehtinen <petri@digip.org>
+ * Copyright (c) 2009-2013 Petri Lehtinen <petri@digip.org>
  *
  * Jansson is free software; you can redistribute it and/or modify
  * it under the terms of the MIT license. See LICENSE for details.
  */
 
+#ifndef _GNU_SOURCE
 #define _GNU_SOURCE
+#endif
+
 #include <stdlib.h>
 #include <string.h>
 #include "jansson_private.h"
@@ -31,7 +34,9 @@ int strbuffer_init(strbuffer_t *strbuff)
 
 void strbuffer_close(strbuffer_t *strbuff)
 {
-    jsonp_free(strbuff->value);
+    if(strbuff->value)
+        jsonp_free(strbuff->value);
+
     strbuff->size = 0;
     strbuff->length = 0;
     strbuff->value = NULL;
@@ -51,7 +56,7 @@ const char *strbuffer_value(const strbuffer_t *strbuff)
 char *strbuffer_steal_value(strbuffer_t *strbuff)
 {
     char *result = strbuff->value;
-    strbuffer_init(strbuff);
+    strbuff->value = NULL;
     return result;
 }
 

+ 3 - 1
compat/jansson/strbuffer.h → compat/jansson-2.5/src/strbuffer.h

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009-2012 Petri Lehtinen <petri@digip.org>
+ * Copyright (c) 2009-2013 Petri Lehtinen <petri@digip.org>
  *
  * Jansson is free software; you can redistribute it and/or modify
  * it under the terms of the MIT license. See LICENSE for details.
@@ -20,6 +20,8 @@ void strbuffer_close(strbuffer_t *strbuff);
 void strbuffer_clear(strbuffer_t *strbuff);
 
 const char *strbuffer_value(const strbuffer_t *strbuff);
+
+/* Steal the value and close the strbuffer */
 char *strbuffer_steal_value(strbuffer_t *strbuff);
 
 int strbuffer_append(strbuffer_t *strbuff, const char *string);

+ 5 - 0
compat/jansson/strconv.c → compat/jansson-2.5/src/strconv.c

@@ -5,6 +5,11 @@
 #include "jansson_private.h"
 #include "strbuffer.h"
 
+/* need config.h to get the correct snprintf */
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
 #if JSON_HAVE_LOCALECONV
 #include <locale.h>
 

+ 1 - 1
compat/jansson/utf.c → compat/jansson-2.5/src/utf.c

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009-2012 Petri Lehtinen <petri@digip.org>
+ * Copyright (c) 2009-2013 Petri Lehtinen <petri@digip.org>
  *
  * Jansson is free software; you can redistribute it and/or modify
  * it under the terms of the MIT license. See LICENSE for details.

+ 1 - 1
compat/jansson/utf.h → compat/jansson-2.5/src/utf.h

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009-2012 Petri Lehtinen <petri@digip.org>
+ * Copyright (c) 2009-2013 Petri Lehtinen <petri@digip.org>
  *
  * Jansson is free software; you can redistribute it and/or modify
  * it under the terms of the MIT license. See LICENSE for details.

+ 26 - 13
compat/jansson/value.c → compat/jansson-2.5/src/value.c

@@ -1,11 +1,13 @@
 /*
- * Copyright (c) 2009-2012 Petri Lehtinen <petri@digip.org>
+ * Copyright (c) 2009-2013 Petri Lehtinen <petri@digip.org>
  *
  * Jansson is free software; you can redistribute it and/or modify
  * it under the terms of the MIT license. See LICENSE for details.
  */
 
+#ifndef _GNU_SOURCE
 #define _GNU_SOURCE
+#endif
 
 #include <stddef.h>
 #include <stdlib.h>
@@ -290,19 +292,27 @@ static json_t *json_object_copy(json_t *object)
     return result;
 }
 
-static json_t *json_object_deep_copy(json_t *object)
+static json_t *json_object_deep_copy(const json_t *object)
 {
     json_t *result;
-
-    const char *key;
-    json_t *value;
+    void *iter;
 
     result = json_object();
     if(!result)
         return NULL;
 
-    json_object_foreach(object, key, value)
+    /* Cannot use json_object_foreach because object has to be cast
+       non-const */
+    iter = json_object_iter((json_t *)object);
+    while(iter) {
+        const char *key;
+        const json_t *value;
+        key = json_object_iter_key(iter);
+        value = json_object_iter_value(iter);
+
         json_object_set_new_nocheck(result, key, json_deep_copy(value));
+        iter = json_object_iter_next((json_t *)object, iter);
+    }
 
     return result;
 }
@@ -509,7 +519,10 @@ int json_array_remove(json_t *json, size_t index)
 
     json_decref(array->table[index]);
 
-    array_move(array, index, index + 1, array->entries - index);
+    /* If we're removing the last element, nothing has to be moved */
+    if(index < array->entries - 1)
+        array_move(array, index, index + 1, array->entries - index - 1);
+
     array->entries--;
 
     return 0;
@@ -590,7 +603,7 @@ static json_t *json_array_copy(json_t *array)
     return result;
 }
 
-static json_t *json_array_deep_copy(json_t *array)
+static json_t *json_array_deep_copy(const json_t *array)
 {
     json_t *result;
     size_t i;
@@ -682,7 +695,7 @@ static int json_string_equal(json_t *string1, json_t *string2)
     return strcmp(json_string_value(string1), json_string_value(string2)) == 0;
 }
 
-static json_t *json_string_copy(json_t *string)
+static json_t *json_string_copy(const json_t *string)
 {
     return json_string_nocheck(json_string_value(string));
 }
@@ -729,7 +742,7 @@ static int json_integer_equal(json_t *integer1, json_t *integer2)
     return json_integer_value(integer1) == json_integer_value(integer2);
 }
 
-static json_t *json_integer_copy(json_t *integer)
+static json_t *json_integer_copy(const json_t *integer)
 {
     return json_integer(json_integer_value(integer));
 }
@@ -781,7 +794,7 @@ static int json_real_equal(json_t *real1, json_t *real2)
     return json_real_value(real1) == json_real_value(real2);
 }
 
-static json_t *json_real_copy(json_t *real)
+static json_t *json_real_copy(const json_t *real)
 {
     return json_real(json_real_value(real));
 }
@@ -907,7 +920,7 @@ json_t *json_copy(json_t *json)
     return NULL;
 }
 
-json_t *json_deep_copy(json_t *json)
+json_t *json_deep_copy(const json_t *json)
 {
     if(!json)
         return NULL;
@@ -931,7 +944,7 @@ json_t *json_deep_copy(json_t *json)
         return json_real_copy(json);
 
     if(json_is_true(json) || json_is_false(json) || json_is_null(json))
-        return json;
+        return (json_t *)json;
 
     return NULL;
 }

+ 0 - 3
compat/jansson/.gitignore

@@ -1,3 +0,0 @@
-
-libjansson.a
-

+ 0 - 21
compat/jansson/Makefile.am

@@ -1,21 +0,0 @@
-EXTRA_DIST = jansson.def
-
-include_HEADERS = jansson.h jansson_config.h
-
-noinst_LIBRARIES	= libjansson.a
-
-libjansson_a_SOURCES	= \
-			  dump.c		\
-			  error.c		\
-			  hashtable.c		\
-			  hashtable.h		\
-			  jansson_private.h	\
-			  load.c		\
-			  memory.c		\
-			  pack_unpack.c		\
-			  strbuffer.c		\
-			  strbuffer.h		\
-			  strconv.c 		\
-			  utf.c			\
-			  utf.h			\
-			  value.c

+ 0 - 39
compat/jansson/jansson_config.h

@@ -1,39 +0,0 @@
-/*
- * Copyright (c) 2010-2012 Petri Lehtinen <petri@digip.org>
- *
- * Jansson is free software; you can redistribute it and/or modify
- * it under the terms of the MIT license. See LICENSE for details.
- *
- *
- * This file specifies a part of the site-specific configuration for
- * Jansson, namely those things that affect the public API in
- * jansson.h.
- *
- * The configure script copies this file to jansson_config.h and
- * replaces @var@ substitutions by values that fit your system. If you
- * cannot run the configure script, you can do the value substitution
- * by hand.
- */
-
-#ifndef JANSSON_CONFIG_H
-#define JANSSON_CONFIG_H
-
-/* If your compiler supports the inline keyword in C, JSON_INLINE is
-   defined to `inline', otherwise empty. In C++, the inline is always
-   supported. */
-#ifdef __cplusplus
-#define JSON_INLINE inline
-#else
-#define JSON_INLINE inline
-#endif
-
-/* If your compiler supports the `long long` type and the strtoll()
-   library function, JSON_INTEGER_IS_LONG_LONG is defined to 1,
-   otherwise to 0. */
-#define JSON_INTEGER_IS_LONG_LONG 1
-
-/* If locale.h and localeconv() are available, define to 1,
-   otherwise to 0. */
-#define JSON_HAVE_LOCALECONV 1
-
-#endif

+ 0 - 13
compat/jansson/util.h

@@ -1,13 +0,0 @@
-/*
- * Copyright (c) 2009, 2010 Petri Lehtinen <petri@digip.org>
- *
- * Jansson is free software; you can redistribute it and/or modify
- * it under the terms of the MIT license. See LICENSE for details.
- */
-
-#ifndef UTIL_H
-#define UTIL_H
-
-#define max(a, b)  ((a) > (b) ? (a) : (b))
-
-#endif

+ 0 - 10
compat/libusb-1.0/doc/Makefile.am

@@ -1,10 +0,0 @@
-EXTRA_DIST = doxygen.cfg.in
-
-docs: doxygen.cfg
-	doxygen $^
-
-docs-upload: docs
-	ln -s html api-1.0
-	rsync -av api-1.0/ web.sourceforge.net:htdocs/api-1.0/
-	rm -f api-1.0
-

+ 0 - 1294
compat/libusb-1.0/doc/doxygen.cfg.in

@@ -1,1294 +0,0 @@
-# Doxyfile 1.5.3
-
-# This file describes the settings to be used by the documentation system
-# doxygen (www.doxygen.org) for a project
-#
-# All text after a hash (#) is considered a comment and will be ignored
-# The format is:
-#       TAG = value [value, ...]
-# For lists items can also be appended using:
-#       TAG += value [value, ...]
-# Values that contain spaces should be placed between quotes (" ")
-
-#---------------------------------------------------------------------------
-# Project related configuration options
-#---------------------------------------------------------------------------
-
-# This tag specifies the encoding used for all characters in the config file that 
-# follow. The default is UTF-8 which is also the encoding used for all text before 
-# the first occurrence of this tag. Doxygen uses libiconv (or the iconv built into 
-# libc) for the transcoding. See http://www.gnu.org/software/libiconv for the list of 
-# possible encodings.
-
-DOXYFILE_ENCODING      = UTF-8
-
-# The PROJECT_NAME tag is a single word (or a sequence of words surrounded 
-# by quotes) that should identify the project.
-
-PROJECT_NAME           = libusb
-
-# The PROJECT_NUMBER tag can be used to enter a project or revision number. 
-# This could be handy for archiving the generated documentation or 
-# if some version control system is used.
-
-PROJECT_NUMBER         = 
-
-# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) 
-# base path where the generated documentation will be put. 
-# If a relative path is entered, it will be relative to the location 
-# where doxygen was started. If left blank the current directory will be used.
-
-OUTPUT_DIRECTORY       = 
-
-# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create 
-# 4096 sub-directories (in 2 levels) under the output directory of each output 
-# format and will distribute the generated files over these directories. 
-# Enabling this option can be useful when feeding doxygen a huge amount of 
-# source files, where putting all generated files in the same directory would 
-# otherwise cause performance problems for the file system.
-
-CREATE_SUBDIRS         = NO
-
-# The OUTPUT_LANGUAGE tag is used to specify the language in which all 
-# documentation generated by doxygen is written. Doxygen will use this 
-# information to generate all constant output in the proper language. 
-# The default language is English, other supported languages are: 
-# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, 
-# Croatian, Czech, Danish, Dutch, Finnish, French, German, Greek, Hungarian, 
-# Italian, Japanese, Japanese-en (Japanese with English messages), Korean, 
-# Korean-en, Lithuanian, Norwegian, Polish, Portuguese, Romanian, Russian, 
-# Serbian, Slovak, Slovene, Spanish, Swedish, and Ukrainian.
-
-OUTPUT_LANGUAGE        = English
-
-# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will 
-# include brief member descriptions after the members that are listed in 
-# the file and class documentation (similar to JavaDoc). 
-# Set to NO to disable this.
-
-BRIEF_MEMBER_DESC      = YES
-
-# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend 
-# the brief description of a member or function before the detailed description. 
-# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the 
-# brief descriptions will be completely suppressed.
-
-REPEAT_BRIEF           = YES
-
-# This tag implements a quasi-intelligent brief description abbreviator 
-# that is used to form the text in various listings. Each string 
-# in this list, if found as the leading text of the brief description, will be 
-# stripped from the text and the result after processing the whole list, is 
-# used as the annotated text. Otherwise, the brief description is used as-is. 
-# If left blank, the following values are used ("$name" is automatically 
-# replaced with the name of the entity): "The $name class" "The $name widget" 
-# "The $name file" "is" "provides" "specifies" "contains" 
-# "represents" "a" "an" "the"
-
-ABBREVIATE_BRIEF       = 
-
-# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then 
-# Doxygen will generate a detailed section even if there is only a brief 
-# description.
-
-ALWAYS_DETAILED_SEC    = NO
-
-# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all 
-# inherited members of a class in the documentation of that class as if those 
-# members were ordinary class members. Constructors, destructors and assignment 
-# operators of the base classes will not be shown.
-
-INLINE_INHERITED_MEMB  = NO
-
-# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full 
-# path before files name in the file list and in the header files. If set 
-# to NO the shortest path that makes the file name unique will be used.
-
-FULL_PATH_NAMES        = NO
-
-# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag 
-# can be used to strip a user-defined part of the path. Stripping is 
-# only done if one of the specified strings matches the left-hand part of 
-# the path. The tag can be used to show relative paths in the file list. 
-# If left blank the directory from which doxygen is run is used as the 
-# path to strip.
-
-STRIP_FROM_PATH        = 
-
-# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of 
-# the path mentioned in the documentation of a class, which tells 
-# the reader which header file to include in order to use a class. 
-# If left blank only the name of the header file containing the class 
-# definition is used. Otherwise one should specify the include paths that 
-# are normally passed to the compiler using the -I flag.
-
-STRIP_FROM_INC_PATH    = 
-
-# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter 
-# (but less readable) file names. This can be useful is your file systems 
-# doesn't support long names like on DOS, Mac, or CD-ROM.
-
-SHORT_NAMES            = NO
-
-# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen 
-# will interpret the first line (until the first dot) of a JavaDoc-style 
-# comment as the brief description. If set to NO, the JavaDoc 
-# comments will behave just like regular Qt-style comments 
-# (thus requiring an explicit @brief command for a brief description.)
-
-JAVADOC_AUTOBRIEF      = YES
-
-# If the QT_AUTOBRIEF tag is set to YES then Doxygen will 
-# interpret the first line (until the first dot) of a Qt-style 
-# comment as the brief description. If set to NO, the comments 
-# will behave just like regular Qt-style comments (thus requiring 
-# an explicit \brief command for a brief description.)
-
-QT_AUTOBRIEF           = NO
-
-# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen 
-# treat a multi-line C++ special comment block (i.e. a block of //! or /// 
-# comments) as a brief description. This used to be the default behaviour. 
-# The new default is to treat a multi-line C++ comment block as a detailed 
-# description. Set this tag to YES if you prefer the old behaviour instead.
-
-MULTILINE_CPP_IS_BRIEF = NO
-
-# If the DETAILS_AT_TOP tag is set to YES then Doxygen 
-# will output the detailed description near the top, like JavaDoc.
-# If set to NO, the detailed description appears after the member 
-# documentation.
-
-DETAILS_AT_TOP         = NO
-
-# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented 
-# member inherits the documentation from any documented member that it 
-# re-implements.
-
-INHERIT_DOCS           = YES
-
-# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce 
-# a new page for each member. If set to NO, the documentation of a member will 
-# be part of the file/class/namespace that contains it.
-
-SEPARATE_MEMBER_PAGES  = NO
-
-# The TAB_SIZE tag can be used to set the number of spaces in a tab. 
-# Doxygen uses this value to replace tabs by spaces in code fragments.
-
-TAB_SIZE               = 4
-
-# This tag can be used to specify a number of aliases that acts 
-# as commands in the documentation. An alias has the form "name=value". 
-# For example adding "sideeffect=\par Side Effects:\n" will allow you to 
-# put the command \sideeffect (or @sideeffect) in the documentation, which 
-# will result in a user-defined paragraph with heading "Side Effects:". 
-# You can put \n's in the value part of an alias to insert newlines.
-
-ALIASES                = 
-
-# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C 
-# sources only. Doxygen will then generate output that is more tailored for C. 
-# For instance, some of the names that are used will be different. The list 
-# of all members will be omitted, etc.
-
-OPTIMIZE_OUTPUT_FOR_C  = YES
-
-# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java 
-# sources only. Doxygen will then generate output that is more tailored for Java. 
-# For instance, namespaces will be presented as packages, qualified scopes 
-# will look different, etc.
-
-OPTIMIZE_OUTPUT_JAVA   = NO
-
-# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want to 
-# include (a tag file for) the STL sources as input, then you should 
-# set this tag to YES in order to let doxygen match functions declarations and 
-# definitions whose arguments contain STL classes (e.g. func(std::string); v.s. 
-# func(std::string) {}). This also make the inheritance and collaboration 
-# diagrams that involve STL classes more complete and accurate.
-
-BUILTIN_STL_SUPPORT    = NO
-
-# If you use Microsoft's C++/CLI language, you should set this option to YES to
-# enable parsing support.
-
-CPP_CLI_SUPPORT        = NO
-
-# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC 
-# tag is set to YES, then doxygen will reuse the documentation of the first 
-# member in the group (if any) for the other members of the group. By default 
-# all members of a group must be documented explicitly.
-
-DISTRIBUTE_GROUP_DOC   = NO
-
-# Set the SUBGROUPING tag to YES (the default) to allow class member groups of 
-# the same type (for instance a group of public functions) to be put as a 
-# subgroup of that type (e.g. under the Public Functions section). Set it to 
-# NO to prevent subgrouping. Alternatively, this can be done per class using 
-# the \nosubgrouping command.
-
-SUBGROUPING            = YES
-
-#---------------------------------------------------------------------------
-# Build related configuration options
-#---------------------------------------------------------------------------
-
-# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in 
-# documentation are documented, even if no documentation was available. 
-# Private class members and static file members will be hidden unless 
-# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES
-
-EXTRACT_ALL            = NO
-
-# If the EXTRACT_PRIVATE tag is set to YES all private members of a class 
-# will be included in the documentation.
-
-EXTRACT_PRIVATE        = NO
-
-# If the EXTRACT_STATIC tag is set to YES all static members of a file 
-# will be included in the documentation.
-
-EXTRACT_STATIC         = YES
-
-# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) 
-# defined locally in source files will be included in the documentation. 
-# If set to NO only classes defined in header files are included.
-
-EXTRACT_LOCAL_CLASSES  = YES
-
-# This flag is only useful for Objective-C code. When set to YES local 
-# methods, which are defined in the implementation section but not in 
-# the interface are included in the documentation. 
-# If set to NO (the default) only methods in the interface are included.
-
-EXTRACT_LOCAL_METHODS  = NO
-
-# If this flag is set to YES, the members of anonymous namespaces will be extracted 
-# and appear in the documentation as a namespace called 'anonymous_namespace{file}', 
-# where file will be replaced with the base name of the file that contains the anonymous 
-# namespace. By default anonymous namespace are hidden.
-
-EXTRACT_ANON_NSPACES   = NO
-
-# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all 
-# undocumented members of documented classes, files or namespaces. 
-# If set to NO (the default) these members will be included in the 
-# various overviews, but no documentation section is generated. 
-# This option has no effect if EXTRACT_ALL is enabled.
-
-HIDE_UNDOC_MEMBERS     = NO
-
-# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all 
-# undocumented classes that are normally visible in the class hierarchy. 
-# If set to NO (the default) these classes will be included in the various 
-# overviews. This option has no effect if EXTRACT_ALL is enabled.
-
-HIDE_UNDOC_CLASSES     = NO
-
-# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all 
-# friend (class|struct|union) declarations. 
-# If set to NO (the default) these declarations will be included in the 
-# documentation.
-
-HIDE_FRIEND_COMPOUNDS  = NO
-
-# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any 
-# documentation blocks found inside the body of a function. 
-# If set to NO (the default) these blocks will be appended to the 
-# function's detailed documentation block.
-
-HIDE_IN_BODY_DOCS      = NO
-
-# The INTERNAL_DOCS tag determines if documentation 
-# that is typed after a \internal command is included. If the tag is set 
-# to NO (the default) then the documentation will be excluded. 
-# Set it to YES to include the internal documentation.
-
-INTERNAL_DOCS          = NO
-
-# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate 
-# file names in lower-case letters. If set to YES upper-case letters are also 
-# allowed. This is useful if you have classes or files whose names only differ 
-# in case and if your file system supports case sensitive file names. Windows 
-# and Mac users are advised to set this option to NO.
-
-CASE_SENSE_NAMES       = YES
-
-# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen 
-# will show members with their full class and namespace scopes in the 
-# documentation. If set to YES the scope will be hidden.
-
-HIDE_SCOPE_NAMES       = NO
-
-# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen 
-# will put a list of the files that are included by a file in the documentation 
-# of that file.
-
-SHOW_INCLUDE_FILES     = YES
-
-# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] 
-# is inserted in the documentation for inline members.
-
-INLINE_INFO            = YES
-
-# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen 
-# will sort the (detailed) documentation of file and class members 
-# alphabetically by member name. If set to NO the members will appear in 
-# declaration order.
-
-SORT_MEMBER_DOCS       = NO
-
-# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the 
-# brief documentation of file, namespace and class members alphabetically 
-# by member name. If set to NO (the default) the members will appear in 
-# declaration order.
-
-SORT_BRIEF_DOCS        = NO
-
-# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be 
-# sorted by fully-qualified names, including namespaces. If set to 
-# NO (the default), the class list will be sorted only by class name, 
-# not including the namespace part. 
-# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
-# Note: This option applies only to the class list, not to the 
-# alphabetical list.
-
-SORT_BY_SCOPE_NAME     = NO
-
-# The GENERATE_TODOLIST tag can be used to enable (YES) or 
-# disable (NO) the todo list. This list is created by putting \todo 
-# commands in the documentation.
-
-GENERATE_TODOLIST      = YES
-
-# The GENERATE_TESTLIST tag can be used to enable (YES) or 
-# disable (NO) the test list. This list is created by putting \test 
-# commands in the documentation.
-
-GENERATE_TESTLIST      = YES
-
-# The GENERATE_BUGLIST tag can be used to enable (YES) or 
-# disable (NO) the bug list. This list is created by putting \bug 
-# commands in the documentation.
-
-GENERATE_BUGLIST       = YES
-
-# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or 
-# disable (NO) the deprecated list. This list is created by putting 
-# \deprecated commands in the documentation.
-
-GENERATE_DEPRECATEDLIST= YES
-
-# The ENABLED_SECTIONS tag can be used to enable conditional 
-# documentation sections, marked by \if sectionname ... \endif.
-
-ENABLED_SECTIONS       = 
-
-# The MAX_INITIALIZER_LINES tag determines the maximum number of lines 
-# the initial value of a variable or define consists of for it to appear in 
-# the documentation. If the initializer consists of more lines than specified 
-# here it will be hidden. Use a value of 0 to hide initializers completely. 
-# The appearance of the initializer of individual variables and defines in the 
-# documentation can be controlled using \showinitializer or \hideinitializer 
-# command in the documentation regardless of this setting.
-
-MAX_INITIALIZER_LINES  = 30
-
-# Set the SHOW_USED_FILES tag to NO to disable the list of files generated 
-# at the bottom of the documentation of classes and structs. If set to YES the 
-# list will mention the files that were used to generate the documentation.
-
-SHOW_USED_FILES        = YES
-
-# If the sources in your project are distributed over multiple directories 
-# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy 
-# in the documentation. The default is NO.
-
-SHOW_DIRECTORIES       = NO
-
-# The FILE_VERSION_FILTER tag can be used to specify a program or script that 
-# doxygen should invoke to get the current version for each file (typically from the 
-# version control system). Doxygen will invoke the program by executing (via 
-# popen()) the command <command> <input-file>, where <command> is the value of 
-# the FILE_VERSION_FILTER tag, and <input-file> is the name of an input file 
-# provided by doxygen. Whatever the program writes to standard output 
-# is used as the file version. See the manual for examples.
-
-FILE_VERSION_FILTER    = 
-
-#---------------------------------------------------------------------------
-# configuration options related to warning and progress messages
-#---------------------------------------------------------------------------
-
-# The QUIET tag can be used to turn on/off the messages that are generated 
-# by doxygen. Possible values are YES and NO. If left blank NO is used.
-
-QUIET                  = NO
-
-# The WARNINGS tag can be used to turn on/off the warning messages that are 
-# generated by doxygen. Possible values are YES and NO. If left blank 
-# NO is used.
-
-WARNINGS               = YES
-
-# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings 
-# for undocumented members. If EXTRACT_ALL is set to YES then this flag will 
-# automatically be disabled.
-
-WARN_IF_UNDOCUMENTED   = YES
-
-# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for 
-# potential errors in the documentation, such as not documenting some 
-# parameters in a documented function, or documenting parameters that 
-# don't exist or using markup commands wrongly.
-
-WARN_IF_DOC_ERROR      = YES
-
-# This WARN_NO_PARAMDOC option can be abled to get warnings for 
-# functions that are documented, but have no documentation for their parameters 
-# or return value. If set to NO (the default) doxygen will only warn about 
-# wrong or incomplete parameter documentation, but not about the absence of 
-# documentation.
-
-WARN_NO_PARAMDOC       = NO
-
-# The WARN_FORMAT tag determines the format of the warning messages that 
-# doxygen can produce. The string should contain the $file, $line, and $text 
-# tags, which will be replaced by the file and line number from which the 
-# warning originated and the warning text. Optionally the format may contain 
-# $version, which will be replaced by the version of the file (if it could 
-# be obtained via FILE_VERSION_FILTER)
-
-WARN_FORMAT            = "$file:$line: $text"
-
-# The WARN_LOGFILE tag can be used to specify a file to which warning 
-# and error messages should be written. If left blank the output is written 
-# to stderr.
-
-WARN_LOGFILE           = 
-
-#---------------------------------------------------------------------------
-# configuration options related to the input files
-#---------------------------------------------------------------------------
-
-# The INPUT tag can be used to specify the files and/or directories that contain 
-# documented source files. You may enter file names like "myfile.cpp" or 
-# directories like "/usr/src/myproject". Separate the files or directories 
-# with spaces.
-
-INPUT                  = @top_srcdir@/libusb
-
-# This tag can be used to specify the character encoding of the source files that 
-# doxygen parses. Internally doxygen uses the UTF-8 encoding, which is also the default 
-# input encoding. Doxygen uses libiconv (or the iconv built into libc) for the transcoding. 
-# See http://www.gnu.org/software/libiconv for the list of possible encodings.
-
-INPUT_ENCODING         = UTF-8
-
-# If the value of the INPUT tag contains directories, you can use the 
-# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp 
-# and *.h) to filter out the source-files in the directories. If left 
-# blank the following patterns are tested: 
-# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx 
-# *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py
-
-FILE_PATTERNS          = 
-
-# The RECURSIVE tag can be used to turn specify whether or not subdirectories 
-# should be searched for input files as well. Possible values are YES and NO. 
-# If left blank NO is used.
-
-RECURSIVE              = NO
-
-# The EXCLUDE tag can be used to specify files and/or directories that should 
-# excluded from the INPUT source files. This way you can easily exclude a 
-# subdirectory from a directory tree whose root is specified with the INPUT tag.
-
-EXCLUDE                = @top_srcdir@/libusb/libusbi.h @top_srcdir@/libusb/hotplug.h
-
-# The EXCLUDE_SYMLINKS tag can be used select whether or not files or 
-# directories that are symbolic links (a Unix filesystem feature) are excluded 
-# from the input.
-
-EXCLUDE_SYMLINKS       = NO
-
-# If the value of the INPUT tag contains directories, you can use the 
-# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude 
-# certain files from those directories. Note that the wildcards are matched 
-# against the file with absolute path, so to exclude all test directories 
-# for example use the pattern */test/*
-
-EXCLUDE_PATTERNS       = 
-
-# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names 
-# (namespaces, classes, functions, etc.) that should be excluded from the output. 
-# The symbol name can be a fully qualified name, a word, or if the wildcard * is used, 
-# a substring. Examples: ANamespace, AClass, AClass::ANamespace, ANamespace::*Test
-
-EXCLUDE_SYMBOLS        = 
-
-# The EXAMPLE_PATH tag can be used to specify one or more files or 
-# directories that contain example code fragments that are included (see 
-# the \include command).
-
-EXAMPLE_PATH           = 
-
-# If the value of the EXAMPLE_PATH tag contains directories, you can use the 
-# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp 
-# and *.h) to filter out the source-files in the directories. If left 
-# blank all files are included.
-
-EXAMPLE_PATTERNS       = 
-
-# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be 
-# searched for input files to be used with the \include or \dontinclude 
-# commands irrespective of the value of the RECURSIVE tag. 
-# Possible values are YES and NO. If left blank NO is used.
-
-EXAMPLE_RECURSIVE      = NO
-
-# The IMAGE_PATH tag can be used to specify one or more files or 
-# directories that contain image that are included in the documentation (see 
-# the \image command).
-
-IMAGE_PATH             = 
-
-# The INPUT_FILTER tag can be used to specify a program that doxygen should 
-# invoke to filter for each input file. Doxygen will invoke the filter program 
-# by executing (via popen()) the command <filter> <input-file>, where <filter> 
-# is the value of the INPUT_FILTER tag, and <input-file> is the name of an 
-# input file. Doxygen will then use the output that the filter program writes 
-# to standard output.  If FILTER_PATTERNS is specified, this tag will be 
-# ignored.
-
-INPUT_FILTER           = 
-
-# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern 
-# basis.  Doxygen will compare the file name with each pattern and apply the 
-# filter if there is a match.  The filters are a list of the form: 
-# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further 
-# info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER 
-# is applied to all files.
-
-FILTER_PATTERNS        = 
-
-# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using 
-# INPUT_FILTER) will be used to filter the input files when producing source 
-# files to browse (i.e. when SOURCE_BROWSER is set to YES).
-
-FILTER_SOURCE_FILES    = NO
-
-#---------------------------------------------------------------------------
-# configuration options related to source browsing
-#---------------------------------------------------------------------------
-
-# If the SOURCE_BROWSER tag is set to YES then a list of source files will 
-# be generated. Documented entities will be cross-referenced with these sources. 
-# Note: To get rid of all source code in the generated output, make sure also 
-# VERBATIM_HEADERS is set to NO. If you have enabled CALL_GRAPH or CALLER_GRAPH 
-# then you must also enable this option. If you don't then doxygen will produce 
-# a warning and turn it on anyway
-
-SOURCE_BROWSER         = NO
-
-# Setting the INLINE_SOURCES tag to YES will include the body 
-# of functions and classes directly in the documentation.
-
-INLINE_SOURCES         = NO
-
-# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct 
-# doxygen to hide any special comment blocks from generated source code 
-# fragments. Normal C and C++ comments will always remain visible.
-
-STRIP_CODE_COMMENTS    = YES
-
-# If the REFERENCED_BY_RELATION tag is set to YES (the default) 
-# then for each documented function all documented 
-# functions referencing it will be listed.
-
-REFERENCED_BY_RELATION = NO
-
-# If the REFERENCES_RELATION tag is set to YES (the default) 
-# then for each documented function all documented entities 
-# called/used by that function will be listed.
-
-REFERENCES_RELATION    = NO
-
-# If the REFERENCES_LINK_SOURCE tag is set to YES (the default)
-# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from
-# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will
-# link to the source code.  Otherwise they will link to the documentstion.
-
-REFERENCES_LINK_SOURCE = YES
-
-# If the USE_HTAGS tag is set to YES then the references to source code 
-# will point to the HTML generated by the htags(1) tool instead of doxygen 
-# built-in source browser. The htags tool is part of GNU's global source 
-# tagging system (see http://www.gnu.org/software/global/global.html). You 
-# will need version 4.8.6 or higher.
-
-USE_HTAGS              = NO
-
-# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen 
-# will generate a verbatim copy of the header file for each class for 
-# which an include is specified. Set to NO to disable this.
-
-VERBATIM_HEADERS       = YES
-
-#---------------------------------------------------------------------------
-# configuration options related to the alphabetical class index
-#---------------------------------------------------------------------------
-
-# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index 
-# of all compounds will be generated. Enable this if the project 
-# contains a lot of classes, structs, unions or interfaces.
-
-ALPHABETICAL_INDEX     = YES
-
-# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then 
-# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns 
-# in which this list will be split (can be a number in the range [1..20])
-
-COLS_IN_ALPHA_INDEX    = 5
-
-# In case all classes in a project start with a common prefix, all 
-# classes will be put under the same header in the alphabetical index. 
-# The IGNORE_PREFIX tag can be used to specify one or more prefixes that 
-# should be ignored while generating the index headers.
-
-IGNORE_PREFIX          = 
-
-#---------------------------------------------------------------------------
-# configuration options related to the HTML output
-#---------------------------------------------------------------------------
-
-# If the GENERATE_HTML tag is set to YES (the default) Doxygen will 
-# generate HTML output.
-
-GENERATE_HTML          = YES
-
-# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. 
-# If a relative path is entered the value of OUTPUT_DIRECTORY will be 
-# put in front of it. If left blank `html' will be used as the default path.
-
-HTML_OUTPUT            = html
-
-# The HTML_FILE_EXTENSION tag can be used to specify the file extension for 
-# each generated HTML page (for example: .htm,.php,.asp). If it is left blank 
-# doxygen will generate files with .html extension.
-
-HTML_FILE_EXTENSION    = .html
-
-# The HTML_HEADER tag can be used to specify a personal HTML header for 
-# each generated HTML page. If it is left blank doxygen will generate a 
-# standard header.
-
-HTML_HEADER            = 
-
-# The HTML_FOOTER tag can be used to specify a personal HTML footer for 
-# each generated HTML page. If it is left blank doxygen will generate a 
-# standard footer.
-
-HTML_FOOTER            = 
-
-# The HTML_STYLESHEET tag can be used to specify a user-defined cascading 
-# style sheet that is used by each HTML page. It can be used to 
-# fine-tune the look of the HTML output. If the tag is left blank doxygen 
-# will generate a default style sheet. Note that doxygen will try to copy 
-# the style sheet file to the HTML output directory, so don't put your own 
-# stylesheet in the HTML output directory as well, or it will be erased!
-
-HTML_STYLESHEET        = 
-
-# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, 
-# files or namespaces will be aligned in HTML using tables. If set to 
-# NO a bullet list will be used.
-
-HTML_ALIGN_MEMBERS     = YES
-
-# If the GENERATE_HTMLHELP tag is set to YES, additional index files 
-# will be generated that can be used as input for tools like the 
-# Microsoft HTML help workshop to generate a compressed HTML help file (.chm) 
-# of the generated HTML documentation.
-
-GENERATE_HTMLHELP      = NO
-
-# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML 
-# documentation will contain sections that can be hidden and shown after the 
-# page has loaded. For this to work a browser that supports 
-# JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox 
-# Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari).
-
-HTML_DYNAMIC_SECTIONS  = YES
-
-# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can 
-# be used to specify the file name of the resulting .chm file. You 
-# can add a path in front of the file if the result should not be 
-# written to the html output directory.
-
-CHM_FILE               = 
-
-# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can 
-# be used to specify the location (absolute path including file name) of 
-# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run 
-# the HTML help compiler on the generated index.hhp.
-
-HHC_LOCATION           = 
-
-# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag 
-# controls if a separate .chi index file is generated (YES) or that 
-# it should be included in the master .chm file (NO).
-
-GENERATE_CHI           = NO
-
-# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag 
-# controls whether a binary table of contents is generated (YES) or a 
-# normal table of contents (NO) in the .chm file.
-
-BINARY_TOC             = NO
-
-# The TOC_EXPAND flag can be set to YES to add extra items for group members 
-# to the contents of the HTML help documentation and to the tree view.
-
-TOC_EXPAND             = NO
-
-# The DISABLE_INDEX tag can be used to turn on/off the condensed index at 
-# top of each HTML page. The value NO (the default) enables the index and 
-# the value YES disables it.
-
-DISABLE_INDEX          = NO
-
-# This tag can be used to set the number of enum values (range [1..20]) 
-# that doxygen will group on one line in the generated HTML documentation.
-
-ENUM_VALUES_PER_LINE   = 4
-
-# If the GENERATE_TREEVIEW tag is set to YES, a side panel will be
-# generated containing a tree-like index structure (just like the one that 
-# is generated for HTML Help). For this to work a browser that supports 
-# JavaScript, DHTML, CSS and frames is required (for instance Mozilla 1.0+, 
-# Netscape 6.0+, Internet explorer 5.0+, or Konqueror). Windows users are 
-# probably better off using the HTML help feature.
-
-GENERATE_TREEVIEW      = NO
-
-# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be 
-# used to set the initial width (in pixels) of the frame in which the tree 
-# is shown.
-
-TREEVIEW_WIDTH         = 250
-
-#---------------------------------------------------------------------------
-# configuration options related to the LaTeX output
-#---------------------------------------------------------------------------
-
-# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will 
-# generate Latex output.
-
-GENERATE_LATEX         = NO
-
-# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. 
-# If a relative path is entered the value of OUTPUT_DIRECTORY will be 
-# put in front of it. If left blank `latex' will be used as the default path.
-
-LATEX_OUTPUT           = latex
-
-# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be 
-# invoked. If left blank `latex' will be used as the default command name.
-
-LATEX_CMD_NAME         = latex
-
-# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to 
-# generate index for LaTeX. If left blank `makeindex' will be used as the 
-# default command name.
-
-MAKEINDEX_CMD_NAME     = makeindex
-
-# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact 
-# LaTeX documents. This may be useful for small projects and may help to 
-# save some trees in general.
-
-COMPACT_LATEX          = NO
-
-# The PAPER_TYPE tag can be used to set the paper type that is used 
-# by the printer. Possible values are: a4, a4wide, letter, legal and 
-# executive. If left blank a4wide will be used.
-
-PAPER_TYPE             = a4wide
-
-# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX 
-# packages that should be included in the LaTeX output.
-
-EXTRA_PACKAGES         = 
-
-# The LATEX_HEADER tag can be used to specify a personal LaTeX header for 
-# the generated latex document. The header should contain everything until 
-# the first chapter. If it is left blank doxygen will generate a 
-# standard header. Notice: only use this tag if you know what you are doing!
-
-LATEX_HEADER           = 
-
-# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated 
-# is prepared for conversion to pdf (using ps2pdf). The pdf file will 
-# contain links (just like the HTML output) instead of page references 
-# This makes the output suitable for online browsing using a pdf viewer.
-
-PDF_HYPERLINKS         = NO
-
-# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of 
-# plain latex in the generated Makefile. Set this option to YES to get a 
-# higher quality PDF documentation.
-
-USE_PDFLATEX           = NO
-
-# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. 
-# command to the generated LaTeX files. This will instruct LaTeX to keep 
-# running if errors occur, instead of asking the user for help. 
-# This option is also used when generating formulas in HTML.
-
-LATEX_BATCHMODE        = NO
-
-# If LATEX_HIDE_INDICES is set to YES then doxygen will not 
-# include the index chapters (such as File Index, Compound Index, etc.) 
-# in the output.
-
-LATEX_HIDE_INDICES     = NO
-
-#---------------------------------------------------------------------------
-# configuration options related to the RTF output
-#---------------------------------------------------------------------------
-
-# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output 
-# The RTF output is optimized for Word 97 and may not look very pretty with 
-# other RTF readers or editors.
-
-GENERATE_RTF           = NO
-
-# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. 
-# If a relative path is entered the value of OUTPUT_DIRECTORY will be 
-# put in front of it. If left blank `rtf' will be used as the default path.
-
-RTF_OUTPUT             = rtf
-
-# If the COMPACT_RTF tag is set to YES Doxygen generates more compact 
-# RTF documents. This may be useful for small projects and may help to 
-# save some trees in general.
-
-COMPACT_RTF            = NO
-
-# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated 
-# will contain hyperlink fields. The RTF file will 
-# contain links (just like the HTML output) instead of page references. 
-# This makes the output suitable for online browsing using WORD or other 
-# programs which support those fields. 
-# Note: wordpad (write) and others do not support links.
-
-RTF_HYPERLINKS         = NO
-
-# Load stylesheet definitions from file. Syntax is similar to doxygen's 
-# config file, i.e. a series of assignments. You only have to provide 
-# replacements, missing definitions are set to their default value.
-
-RTF_STYLESHEET_FILE    = 
-
-# Set optional variables used in the generation of an rtf document. 
-# Syntax is similar to doxygen's config file.
-
-RTF_EXTENSIONS_FILE    = 
-
-#---------------------------------------------------------------------------
-# configuration options related to the man page output
-#---------------------------------------------------------------------------
-
-# If the GENERATE_MAN tag is set to YES (the default) Doxygen will 
-# generate man pages
-
-GENERATE_MAN           = NO
-
-# The MAN_OUTPUT tag is used to specify where the man pages will be put. 
-# If a relative path is entered the value of OUTPUT_DIRECTORY will be 
-# put in front of it. If left blank `man' will be used as the default path.
-
-MAN_OUTPUT             = man
-
-# The MAN_EXTENSION tag determines the extension that is added to 
-# the generated man pages (default is the subroutine's section .3)
-
-MAN_EXTENSION          = .3
-
-# If the MAN_LINKS tag is set to YES and Doxygen generates man output, 
-# then it will generate one additional man file for each entity 
-# documented in the real man page(s). These additional files 
-# only source the real man page, but without them the man command 
-# would be unable to find the correct page. The default is NO.
-
-MAN_LINKS              = NO
-
-#---------------------------------------------------------------------------
-# configuration options related to the XML output
-#---------------------------------------------------------------------------
-
-# If the GENERATE_XML tag is set to YES Doxygen will 
-# generate an XML file that captures the structure of 
-# the code including all documentation.
-
-GENERATE_XML           = NO
-
-# The XML_OUTPUT tag is used to specify where the XML pages will be put. 
-# If a relative path is entered the value of OUTPUT_DIRECTORY will be 
-# put in front of it. If left blank `xml' will be used as the default path.
-
-XML_OUTPUT             = xml
-
-# The XML_SCHEMA tag can be used to specify an XML schema, 
-# which can be used by a validating XML parser to check the 
-# syntax of the XML files.
-
-XML_SCHEMA             = 
-
-# The XML_DTD tag can be used to specify an XML DTD, 
-# which can be used by a validating XML parser to check the 
-# syntax of the XML files.
-
-XML_DTD                = 
-
-# If the XML_PROGRAMLISTING tag is set to YES Doxygen will 
-# dump the program listings (including syntax highlighting 
-# and cross-referencing information) to the XML output. Note that 
-# enabling this will significantly increase the size of the XML output.
-
-XML_PROGRAMLISTING     = YES
-
-#---------------------------------------------------------------------------
-# configuration options for the AutoGen Definitions output
-#---------------------------------------------------------------------------
-
-# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will 
-# generate an AutoGen Definitions (see autogen.sf.net) file 
-# that captures the structure of the code including all 
-# documentation. Note that this feature is still experimental 
-# and incomplete at the moment.
-
-GENERATE_AUTOGEN_DEF   = NO
-
-#---------------------------------------------------------------------------
-# configuration options related to the Perl module output
-#---------------------------------------------------------------------------
-
-# If the GENERATE_PERLMOD tag is set to YES Doxygen will 
-# generate a Perl module file that captures the structure of 
-# the code including all documentation. Note that this 
-# feature is still experimental and incomplete at the 
-# moment.
-
-GENERATE_PERLMOD       = NO
-
-# If the PERLMOD_LATEX tag is set to YES Doxygen will generate 
-# the necessary Makefile rules, Perl scripts and LaTeX code to be able 
-# to generate PDF and DVI output from the Perl module output.
-
-PERLMOD_LATEX          = NO
-
-# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be 
-# nicely formatted so it can be parsed by a human reader.  This is useful 
-# if you want to understand what is going on.  On the other hand, if this 
-# tag is set to NO the size of the Perl module output will be much smaller 
-# and Perl will parse it just the same.
-
-PERLMOD_PRETTY         = YES
-
-# The names of the make variables in the generated doxyrules.make file 
-# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. 
-# This is useful so different doxyrules.make files included by the same 
-# Makefile don't overwrite each other's variables.
-
-PERLMOD_MAKEVAR_PREFIX = 
-
-#---------------------------------------------------------------------------
-# Configuration options related to the preprocessor   
-#---------------------------------------------------------------------------
-
-# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will 
-# evaluate all C-preprocessor directives found in the sources and include 
-# files.
-
-ENABLE_PREPROCESSING   = YES
-
-# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro 
-# names in the source code. If set to NO (the default) only conditional 
-# compilation will be performed. Macro expansion can be done in a controlled 
-# way by setting EXPAND_ONLY_PREDEF to YES.
-
-MACRO_EXPANSION        = YES
-
-# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES 
-# then the macro expansion is limited to the macros specified with the 
-# PREDEFINED and EXPAND_AS_DEFINED tags.
-
-EXPAND_ONLY_PREDEF     = YES
-
-# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files 
-# in the INCLUDE_PATH (see below) will be search if a #include is found.
-
-SEARCH_INCLUDES        = YES
-
-# The INCLUDE_PATH tag can be used to specify one or more directories that 
-# contain include files that are not input files but should be processed by 
-# the preprocessor.
-
-INCLUDE_PATH           = 
-
-# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard 
-# patterns (like *.h and *.hpp) to filter out the header-files in the 
-# directories. If left blank, the patterns specified with FILE_PATTERNS will 
-# be used.
-
-INCLUDE_FILE_PATTERNS  = 
-
-# The PREDEFINED tag can be used to specify one or more macro names that 
-# are defined before the preprocessor is started (similar to the -D option of 
-# gcc). The argument of the tag is a list of macros of the form: name 
-# or name=definition (no spaces). If the definition and the = are 
-# omitted =1 is assumed. To prevent a macro definition from being 
-# undefined via #undef or recursively expanded use the := operator 
-# instead of the = operator.
-
-PREDEFINED             = API_EXPORTED= LIBUSB_CALL= DEFAULT_VISIBILITY=
-
-# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then 
-# this tag can be used to specify a list of macro names that should be expanded. 
-# The macro definition that is found in the sources will be used. 
-# Use the PREDEFINED tag if you want to use a different macro definition.
-
-EXPAND_AS_DEFINED      = 
-
-# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then 
-# doxygen's preprocessor will remove all function-like macros that are alone 
-# on a line, have an all uppercase name, and do not end with a semicolon. Such 
-# function macros are typically used for boiler-plate code, and will confuse 
-# the parser if not removed.
-
-SKIP_FUNCTION_MACROS   = YES
-
-#---------------------------------------------------------------------------
-# Configuration::additions related to external references   
-#---------------------------------------------------------------------------
-
-# The TAGFILES option can be used to specify one or more tagfiles. 
-# Optionally an initial location of the external documentation 
-# can be added for each tagfile. The format of a tag file without 
-# this location is as follows: 
-#   TAGFILES = file1 file2 ... 
-# Adding location for the tag files is done as follows: 
-#   TAGFILES = file1=loc1 "file2 = loc2" ... 
-# where "loc1" and "loc2" can be relative or absolute paths or 
-# URLs. If a location is present for each tag, the installdox tool 
-# does not have to be run to correct the links.
-# Note that each tag file must have a unique name
-# (where the name does NOT include the path)
-# If a tag file is not located in the directory in which doxygen 
-# is run, you must also specify the path to the tagfile here.
-
-TAGFILES               = 
-
-# When a file name is specified after GENERATE_TAGFILE, doxygen will create 
-# a tag file that is based on the input files it reads.
-
-GENERATE_TAGFILE       = 
-
-# If the ALLEXTERNALS tag is set to YES all external classes will be listed 
-# in the class index. If set to NO only the inherited external classes 
-# will be listed.
-
-ALLEXTERNALS           = NO
-
-# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed 
-# in the modules index. If set to NO, only the current project's groups will 
-# be listed.
-
-EXTERNAL_GROUPS        = YES
-
-# The PERL_PATH should be the absolute path and name of the perl script 
-# interpreter (i.e. the result of `which perl').
-
-PERL_PATH              = /usr/bin/perl
-
-#---------------------------------------------------------------------------
-# Configuration options related to the dot tool   
-#---------------------------------------------------------------------------
-
-# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will 
-# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base 
-# or super classes. Setting the tag to NO turns the diagrams off. Note that 
-# this option is superseded by the HAVE_DOT option below. This is only a 
-# fallback. It is recommended to install and use dot, since it yields more 
-# powerful graphs.
-
-CLASS_DIAGRAMS         = YES
-
-# You can define message sequence charts within doxygen comments using the \msc 
-# command. Doxygen will then run the mscgen tool (see http://www.mcternan.me.uk/mscgen/) to 
-# produce the chart and insert it in the documentation. The MSCGEN_PATH tag allows you to 
-# specify the directory where the mscgen tool resides. If left empty the tool is assumed to 
-# be found in the default search path.
-
-MSCGEN_PATH            = 
-
-# If set to YES, the inheritance and collaboration graphs will hide 
-# inheritance and usage relations if the target is undocumented 
-# or is not a class.
-
-HIDE_UNDOC_RELATIONS   = YES
-
-# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is 
-# available from the path. This tool is part of Graphviz, a graph visualization 
-# toolkit from AT&T and Lucent Bell Labs. The other options in this section 
-# have no effect if this option is set to NO (the default)
-
-HAVE_DOT               = NO
-
-# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen 
-# will generate a graph for each documented class showing the direct and 
-# indirect inheritance relations. Setting this tag to YES will force the 
-# the CLASS_DIAGRAMS tag to NO.
-
-CLASS_GRAPH            = YES
-
-# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen 
-# will generate a graph for each documented class showing the direct and 
-# indirect implementation dependencies (inheritance, containment, and 
-# class references variables) of the class with other documented classes.
-
-COLLABORATION_GRAPH    = YES
-
-# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen 
-# will generate a graph for groups, showing the direct groups dependencies
-
-GROUP_GRAPHS           = YES
-
-# If the UML_LOOK tag is set to YES doxygen will generate inheritance and 
-# collaboration diagrams in a style similar to the OMG's Unified Modeling 
-# Language.
-
-UML_LOOK               = NO
-
-# If set to YES, the inheritance and collaboration graphs will show the 
-# relations between templates and their instances.
-
-TEMPLATE_RELATIONS     = NO
-
-# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT 
-# tags are set to YES then doxygen will generate a graph for each documented 
-# file showing the direct and indirect include dependencies of the file with 
-# other documented files.
-
-INCLUDE_GRAPH          = YES
-
-# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and 
-# HAVE_DOT tags are set to YES then doxygen will generate a graph for each 
-# documented header file showing the documented files that directly or 
-# indirectly include this file.
-
-INCLUDED_BY_GRAPH      = YES
-
-# If the CALL_GRAPH, SOURCE_BROWSER and HAVE_DOT tags are set to YES then doxygen will 
-# generate a call dependency graph for every global function or class method. 
-# Note that enabling this option will significantly increase the time of a run. 
-# So in most cases it will be better to enable call graphs for selected 
-# functions only using the \callgraph command.
-
-CALL_GRAPH             = NO
-
-# If the CALLER_GRAPH, SOURCE_BROWSER and HAVE_DOT tags are set to YES then doxygen will 
-# generate a caller dependency graph for every global function or class method. 
-# Note that enabling this option will significantly increase the time of a run. 
-# So in most cases it will be better to enable caller graphs for selected 
-# functions only using the \callergraph command.
-
-CALLER_GRAPH           = NO
-
-# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen 
-# will graphical hierarchy of all classes instead of a textual one.
-
-GRAPHICAL_HIERARCHY    = YES
-
-# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES 
-# then doxygen will show the dependencies a directory has on other directories 
-# in a graphical way. The dependency relations are determined by the #include
-# relations between the files in the directories.
-
-DIRECTORY_GRAPH        = YES
-
-# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images 
-# generated by dot. Possible values are png, jpg, or gif
-# If left blank png will be used.
-
-DOT_IMAGE_FORMAT       = png
-
-# The tag DOT_PATH can be used to specify the path where the dot tool can be 
-# found. If left blank, it is assumed the dot tool can be found in the path.
-
-DOT_PATH               = 
-
-# The DOTFILE_DIRS tag can be used to specify one or more directories that 
-# contain dot files that are included in the documentation (see the 
-# \dotfile command).
-
-DOTFILE_DIRS           = 
-
-# The MAX_DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of 
-# nodes that will be shown in the graph. If the number of nodes in a graph 
-# becomes larger than this value, doxygen will truncate the graph, which is 
-# visualized by representing a node as a red box. Note that doxygen if the number 
-# of direct children of the root node in a graph is already larger than 
-# MAX_DOT_GRAPH_NOTES then the graph will not be shown at all. Also note 
-# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.
-
-DOT_GRAPH_MAX_NODES    = 50
-
-# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the 
-# graphs generated by dot. A depth value of 3 means that only nodes reachable 
-# from the root by following a path via at most 3 edges will be shown. Nodes 
-# that lay further from the root node will be omitted. Note that setting this 
-# option to 1 or 2 may greatly reduce the computation time needed for large 
-# code bases. Also note that the size of a graph can be further restricted by 
-# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.
-
-MAX_DOT_GRAPH_DEPTH    = 0
-
-# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent 
-# background. This is disabled by default, which results in a white background. 
-# Warning: Depending on the platform used, enabling this option may lead to 
-# badly anti-aliased labels on the edges of a graph (i.e. they become hard to 
-# read).
-
-DOT_TRANSPARENT        = NO
-
-# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output 
-# files in one run (i.e. multiple -o and -T options on the command line). This 
-# makes dot run faster, but since only newer versions of dot (>1.8.10) 
-# support this, this feature is disabled by default.
-
-DOT_MULTI_TARGETS      = NO
-
-# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will 
-# generate a legend page explaining the meaning of the various boxes and 
-# arrows in the dot generated graphs.
-
-GENERATE_LEGEND        = YES
-
-# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will 
-# remove the intermediate dot files that are used to generate 
-# the various graphs.
-
-DOT_CLEANUP            = YES
-
-#---------------------------------------------------------------------------
-# Configuration::additions related to the search engine   
-#---------------------------------------------------------------------------
-
-# The SEARCHENGINE tag specifies whether or not a search engine should be 
-# used. If set to NO the values of all tags below this one will be ignored.
-
-SEARCHENGINE           = NO

+ 0 - 15
compat/libusb-1.0/examples/Makefile.am

@@ -1,15 +0,0 @@
-AM_CPPFLAGS = -I$(top_srcdir)/libusb
-LDADD = ../libusb/libusb-1.0.la
-noinst_PROGRAMS = listdevs hotplugtest testlibusb1
-
-if HAVE_SIGACTION
-noinst_PROGRAMS += dpfp
-
-if THREADS_POSIX
-dpfp_threaded_CFLAGS = $(AM_CFLAGS)
-noinst_PROGRAMS += dpfp_threaded
-endif
-
-sam3u_benchmark_SOURCES = sam3u_benchmark.c
-noinst_PROGRAMS += sam3u_benchmark
-endif

+ 0 - 507
compat/libusb-1.0/examples/dpfp.c

@@ -1,507 +0,0 @@
-/*
- * libusb example program to manipulate U.are.U 4000B fingerprint scanner.
- * Copyright (C) 2007 Daniel Drake <dsd@gentoo.org>
- *
- * Basic image capture program only, does not consider the powerup quirks or
- * the fact that image encryption may be enabled. Not expected to work
- * flawlessly all of the time.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-#include <errno.h>
-#include <signal.h>
-#include <string.h>
-#include <stdio.h>
-#include <stdlib.h>
-
-#include <libusb.h>
-
-#define EP_INTR			(1 | LIBUSB_ENDPOINT_IN)
-#define EP_DATA			(2 | LIBUSB_ENDPOINT_IN)
-#define CTRL_IN			(LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_ENDPOINT_IN)
-#define CTRL_OUT		(LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_ENDPOINT_OUT)
-#define USB_RQ			0x04
-#define INTR_LENGTH		64
-
-enum {
-	MODE_INIT = 0x00,
-	MODE_AWAIT_FINGER_ON = 0x10,
-	MODE_AWAIT_FINGER_OFF = 0x12,
-	MODE_CAPTURE = 0x20,
-	MODE_SHUT_UP = 0x30,
-	MODE_READY = 0x80,
-};
-
-static int next_state(void);
-
-enum {
-	STATE_AWAIT_MODE_CHANGE_AWAIT_FINGER_ON = 1,
-	STATE_AWAIT_IRQ_FINGER_DETECTED,
-	STATE_AWAIT_MODE_CHANGE_CAPTURE,
-	STATE_AWAIT_IMAGE,
-	STATE_AWAIT_MODE_CHANGE_AWAIT_FINGER_OFF,
-	STATE_AWAIT_IRQ_FINGER_REMOVED,
-};
-
-static int state = 0;
-static struct libusb_device_handle *devh = NULL;
-static unsigned char imgbuf[0x1b340];
-static unsigned char irqbuf[INTR_LENGTH];
-static struct libusb_transfer *img_transfer = NULL;
-static struct libusb_transfer *irq_transfer = NULL;
-static int img_idx = 0;
-static int do_exit = 0;
-
-static int find_dpfp_device(void)
-{
-	devh = libusb_open_device_with_vid_pid(NULL, 0x05ba, 0x000a);
-	return devh ? 0 : -EIO;
-}
-
-static int print_f0_data(void)
-{
-	unsigned char data[0x10];
-	int r;
-	unsigned int i;
-
-	r = libusb_control_transfer(devh, CTRL_IN, USB_RQ, 0xf0, 0, data,
-		sizeof(data), 0);
-	if (r < 0) {
-		fprintf(stderr, "F0 error %d\n", r);
-		return r;
-	}
-	if ((unsigned int) r < sizeof(data)) {
-		fprintf(stderr, "short read (%d)\n", r);
-		return -1;
-	}
-
-	printf("F0 data:");
-	for (i = 0; i < sizeof(data); i++)
-		printf("%02x ", data[i]);
-	printf("\n");
-	return 0;
-}
-
-static int get_hwstat(unsigned char *status)
-{
-	int r;
-
-	r = libusb_control_transfer(devh, CTRL_IN, USB_RQ, 0x07, 0, status, 1, 0);
-	if (r < 0) {
-		fprintf(stderr, "read hwstat error %d\n", r);
-		return r;
-	}
-	if ((unsigned int) r < 1) {
-		fprintf(stderr, "short read (%d)\n", r);
-		return -1;
-	}
-
-	printf("hwstat reads %02x\n", *status);
-	return 0;
-}
-
-static int set_hwstat(unsigned char data)
-{
-	int r;
-
-	printf("set hwstat to %02x\n", data);
-	r = libusb_control_transfer(devh, CTRL_OUT, USB_RQ, 0x07, 0, &data, 1, 0);
-	if (r < 0) {
-		fprintf(stderr, "set hwstat error %d\n", r);
-		return r;
-	}
-	if ((unsigned int) r < 1) {
-		fprintf(stderr, "short write (%d)", r);
-		return -1;
-	}
-
-	return 0;
-}
-
-static int set_mode(unsigned char data)
-{
-	int r;
-	printf("set mode %02x\n", data);
-
-	r = libusb_control_transfer(devh, CTRL_OUT, USB_RQ, 0x4e, 0, &data, 1, 0);
-	if (r < 0) {
-		fprintf(stderr, "set mode error %d\n", r);
-		return r;
-	}
-	if ((unsigned int) r < 1) {
-		fprintf(stderr, "short write (%d)", r);
-		return -1;
-	}
-
-	return 0;
-}
-
-static void LIBUSB_CALL cb_mode_changed(struct libusb_transfer *transfer)
-{
-	if (transfer->status != LIBUSB_TRANSFER_COMPLETED) {
-		fprintf(stderr, "mode change transfer not completed!\n");
-		do_exit = 2;
-	}
-
-	printf("async cb_mode_changed length=%d actual_length=%d\n",
-		transfer->length, transfer->actual_length);
-	if (next_state() < 0)
-		do_exit = 2;
-}
-
-static int set_mode_async(unsigned char data)
-{
-	unsigned char *buf = malloc(LIBUSB_CONTROL_SETUP_SIZE + 1);
-	struct libusb_transfer *transfer;
-
-	if (!buf)
-		return -ENOMEM;
-
-	transfer = libusb_alloc_transfer(0);
-	if (!transfer) {
-		free(buf);
-		return -ENOMEM;
-	}
-
-	printf("async set mode %02x\n", data);
-	libusb_fill_control_setup(buf, CTRL_OUT, USB_RQ, 0x4e, 0, 1);
-	buf[LIBUSB_CONTROL_SETUP_SIZE] = data;
-	libusb_fill_control_transfer(transfer, devh, buf, cb_mode_changed, NULL,
-		1000);
-
-	transfer->flags = LIBUSB_TRANSFER_SHORT_NOT_OK
-		| LIBUSB_TRANSFER_FREE_BUFFER | LIBUSB_TRANSFER_FREE_TRANSFER;
-	return libusb_submit_transfer(transfer);
-}
-
-static int do_sync_intr(unsigned char *data)
-{
-	int r;
-	int transferred;
-
-	r = libusb_interrupt_transfer(devh, EP_INTR, data, INTR_LENGTH,
-		&transferred, 1000);
-	if (r < 0) {
-		fprintf(stderr, "intr error %d\n", r);
-		return r;
-	}
-	if (transferred < INTR_LENGTH) {
-		fprintf(stderr, "short read (%d)\n", r);
-		return -1;
-	}
-
-	printf("recv interrupt %04x\n", *((uint16_t *) data));
-	return 0;
-}
-
-static int sync_intr(unsigned char type)
-{
-	int r;
-	unsigned char data[INTR_LENGTH];
-
-	while (1) {
-		r = do_sync_intr(data);
-		if (r < 0)
-			return r;
-		if (data[0] == type)
-			return 0;
-	}
-}
-
-static int save_to_file(unsigned char *data)
-{
-	FILE *fd;
-	char filename[64];
-
-	snprintf(filename, sizeof(filename), "finger%d.pgm", img_idx++);
-	fd = fopen(filename, "w");
-	if (!fd)
-		return -1;
-
-	fputs("P5 384 289 255 ", fd);
-	(void) fwrite(data + 64, 1, 384*289, fd);
-	fclose(fd);
-	printf("saved image to %s\n", filename);
-	return 0;
-}
-
-static int next_state(void)
-{
-	int r = 0;
-	printf("old state: %d\n", state);
-	switch (state) {
-	case STATE_AWAIT_IRQ_FINGER_REMOVED:
-		state = STATE_AWAIT_MODE_CHANGE_AWAIT_FINGER_ON;
-		r = set_mode_async(MODE_AWAIT_FINGER_ON);
-		break;
-	case STATE_AWAIT_MODE_CHANGE_AWAIT_FINGER_ON:
-		state = STATE_AWAIT_IRQ_FINGER_DETECTED;
-		break;
-	case STATE_AWAIT_IRQ_FINGER_DETECTED:
-		state = STATE_AWAIT_MODE_CHANGE_CAPTURE;
-		r = set_mode_async(MODE_CAPTURE);
-		break;
-	case STATE_AWAIT_MODE_CHANGE_CAPTURE:
-		state = STATE_AWAIT_IMAGE;
-		break;
-	case STATE_AWAIT_IMAGE:
-		state = STATE_AWAIT_MODE_CHANGE_AWAIT_FINGER_OFF;
-		r = set_mode_async(MODE_AWAIT_FINGER_OFF);
-		break;
-	case STATE_AWAIT_MODE_CHANGE_AWAIT_FINGER_OFF:
-		state = STATE_AWAIT_IRQ_FINGER_REMOVED;
-		break;
-	default:
-		printf("unrecognised state %d\n", state);
-	}
-	if (r < 0) {
-		fprintf(stderr, "error detected changing state\n");
-		return r;
-	}
-
-	printf("new state: %d\n", state);
-	return 0;
-}
-
-static void LIBUSB_CALL cb_irq(struct libusb_transfer *transfer)
-{
-	unsigned char irqtype = transfer->buffer[0];
-
-	if (transfer->status != LIBUSB_TRANSFER_COMPLETED) {
-		fprintf(stderr, "irq transfer status %d?\n", transfer->status);
-		do_exit = 2;
-		libusb_free_transfer(transfer);
-		irq_transfer = NULL;
-		return;
-	}
-
-	printf("IRQ callback %02x\n", irqtype);
-	switch (state) {
-	case STATE_AWAIT_IRQ_FINGER_DETECTED:
-		if (irqtype == 0x01) {
-			if (next_state() < 0) {
-				do_exit = 2;
-				return;
-			}
-		} else {
-			printf("finger-on-sensor detected in wrong state!\n");
-		}
-		break;
-	case STATE_AWAIT_IRQ_FINGER_REMOVED:
-		if (irqtype == 0x02) {
-			if (next_state() < 0) {
-				do_exit = 2;
-				return;
-			}
-		} else {
-			printf("finger-on-sensor detected in wrong state!\n");
-		}
-		break;
-	}
-	if (libusb_submit_transfer(irq_transfer) < 0)
-		do_exit = 2;
-}
-
-static void LIBUSB_CALL cb_img(struct libusb_transfer *transfer)
-{
-	if (transfer->status != LIBUSB_TRANSFER_COMPLETED) {
-		fprintf(stderr, "img transfer status %d?\n", transfer->status);
-		do_exit = 2;
-		libusb_free_transfer(transfer);
-		img_transfer = NULL;
-		return;
-	}
-
-	printf("Image callback\n");
-	save_to_file(imgbuf);
-	if (next_state() < 0) {
-		do_exit = 2;
-		return;
-	}
-	if (libusb_submit_transfer(img_transfer) < 0)
-		do_exit = 2;
-}
-
-static int init_capture(void)
-{
-	int r;
-
-	r = libusb_submit_transfer(irq_transfer);
-	if (r < 0)
-		return r;
-
-	r = libusb_submit_transfer(img_transfer);
-	if (r < 0) {
-		libusb_cancel_transfer(irq_transfer);
-		while (irq_transfer)
-			if (libusb_handle_events(NULL) < 0)
-				break;
-		return r;
-	}
-
-	/* start state machine */
-	state = STATE_AWAIT_IRQ_FINGER_REMOVED;
-	return next_state();
-}
-
-static int do_init(void)
-{
-	unsigned char status;
-	int r;
-
-	r = get_hwstat(&status);
-	if (r < 0)
-		return r;
-
-	if (!(status & 0x80)) {
-		r = set_hwstat(status | 0x80);
-		if (r < 0)
-			return r;
-		r = get_hwstat(&status);
-		if (r < 0)
-			return r;
-	}
-
-	status &= ~0x80;
-	r = set_hwstat(status);
-	if (r < 0)
-		return r;
-
-	r = get_hwstat(&status);
-	if (r < 0)
-		return r;
-
-	r = sync_intr(0x56);
-	if (r < 0)
-		return r;
-
-	return 0;
-}
-
-static int alloc_transfers(void)
-{
-	img_transfer = libusb_alloc_transfer(0);
-	if (!img_transfer)
-		return -ENOMEM;
-
-	irq_transfer = libusb_alloc_transfer(0);
-	if (!irq_transfer)
-		return -ENOMEM;
-
-	libusb_fill_bulk_transfer(img_transfer, devh, EP_DATA, imgbuf,
-		sizeof(imgbuf), cb_img, NULL, 0);
-	libusb_fill_interrupt_transfer(irq_transfer, devh, EP_INTR, irqbuf,
-		sizeof(irqbuf), cb_irq, NULL, 0);
-
-	return 0;
-}
-
-static void sighandler(int signum)
-{
-	do_exit = 1;
-}
-
-int main(void)
-{
-	struct sigaction sigact;
-	int r = 1;
-
-	r = libusb_init(NULL);
-	if (r < 0) {
-		fprintf(stderr, "failed to initialise libusb\n");
-		exit(1);
-	}
-
-	r = find_dpfp_device();
-	if (r < 0) {
-		fprintf(stderr, "Could not find/open device\n");
-		goto out;
-	}
-
-	r = libusb_claim_interface(devh, 0);
-	if (r < 0) {
-		fprintf(stderr, "usb_claim_interface error %d\n", r);
-		goto out;
-	}
-	printf("claimed interface\n");
-
-	r = print_f0_data();
-	if (r < 0)
-		goto out_release;
-
-	r = do_init();
-	if (r < 0)
-		goto out_deinit;
-
-	/* async from here onwards */
-
-	r = alloc_transfers();
-	if (r < 0)
-		goto out_deinit;
-
-	r = init_capture();
-	if (r < 0)
-		goto out_deinit;
-
-	sigact.sa_handler = sighandler;
-	sigemptyset(&sigact.sa_mask);
-	sigact.sa_flags = 0;
-	sigaction(SIGINT, &sigact, NULL);
-	sigaction(SIGTERM, &sigact, NULL);
-	sigaction(SIGQUIT, &sigact, NULL);
-
-	while (!do_exit) {
-		r = libusb_handle_events(NULL);
-		if (r < 0)
-			goto out_deinit;
-	}
-
-	printf("shutting down...\n");
-
-	if (irq_transfer) {
-		r = libusb_cancel_transfer(irq_transfer);
-		if (r < 0)
-			goto out_deinit;
-	}
-
-	if (img_transfer) {
-		r = libusb_cancel_transfer(img_transfer);
-		if (r < 0)
-			goto out_deinit;
-	}
-
-	while (irq_transfer || img_transfer)
-		if (libusb_handle_events(NULL) < 0)
-			break;
-
-	if (do_exit == 1)
-		r = 0;
-	else
-		r = 1;
-
-out_deinit:
-	libusb_free_transfer(img_transfer);
-	libusb_free_transfer(irq_transfer);
-	set_mode(0);
-	set_hwstat(0x80);
-out_release:
-	libusb_release_interface(devh, 0);
-out:
-	libusb_close(devh);
-	libusb_exit(NULL);
-	return r >= 0 ? r : -r;
-}
-

+ 0 - 545
compat/libusb-1.0/examples/dpfp_threaded.c

@@ -1,545 +0,0 @@
-/*
- * libusb example program to manipulate U.are.U 4000B fingerprint scanner.
- * Copyright (C) 2007 Daniel Drake <dsd@gentoo.org>
- *
- * Basic image capture program only, does not consider the powerup quirks or
- * the fact that image encryption may be enabled. Not expected to work
- * flawlessly all of the time.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-#include <errno.h>
-#include <pthread.h>
-#include <signal.h>
-#include <string.h>
-#include <stdio.h>
-#include <stdlib.h>
-
-#include <libusb.h>
-
-#define EP_INTR			(1 | LIBUSB_ENDPOINT_IN)
-#define EP_DATA			(2 | LIBUSB_ENDPOINT_IN)
-#define CTRL_IN			(LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_ENDPOINT_IN)
-#define CTRL_OUT		(LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_ENDPOINT_OUT)
-#define USB_RQ			0x04
-#define INTR_LENGTH		64
-
-enum {
-	MODE_INIT = 0x00,
-	MODE_AWAIT_FINGER_ON = 0x10,
-	MODE_AWAIT_FINGER_OFF = 0x12,
-	MODE_CAPTURE = 0x20,
-	MODE_SHUT_UP = 0x30,
-	MODE_READY = 0x80,
-};
-
-static int next_state(void);
-
-enum {
-	STATE_AWAIT_MODE_CHANGE_AWAIT_FINGER_ON = 1,
-	STATE_AWAIT_IRQ_FINGER_DETECTED,
-	STATE_AWAIT_MODE_CHANGE_CAPTURE,
-	STATE_AWAIT_IMAGE,
-	STATE_AWAIT_MODE_CHANGE_AWAIT_FINGER_OFF,
-	STATE_AWAIT_IRQ_FINGER_REMOVED,
-};
-
-static int state = 0;
-static struct libusb_device_handle *devh = NULL;
-static unsigned char imgbuf[0x1b340];
-static unsigned char irqbuf[INTR_LENGTH];
-static struct libusb_transfer *img_transfer = NULL;
-static struct libusb_transfer *irq_transfer = NULL;
-static int img_idx = 0;
-static int do_exit = 0;
-
-static pthread_t poll_thread;
-static pthread_cond_t exit_cond = PTHREAD_COND_INITIALIZER;
-static pthread_mutex_t exit_cond_lock = PTHREAD_MUTEX_INITIALIZER;
-
-static void request_exit(int code)
-{
-	do_exit = code;
-	pthread_cond_signal(&exit_cond);
-}
-
-static void *poll_thread_main(void *arg)
-{
-	int r = 0;
-	printf("poll thread running\n");
-
-	while (!do_exit) {
-		struct timeval tv = { 1, 0 };
-		r = libusb_handle_events_timeout(NULL, &tv);
-		if (r < 0) {
-			request_exit(2);
-			break;
-		}
-	}
-
-	printf("poll thread shutting down\n");
-	return NULL;
-}
-
-static int find_dpfp_device(void)
-{
-	devh = libusb_open_device_with_vid_pid(NULL, 0x05ba, 0x000a);
-	return devh ? 0 : -EIO;
-}
-
-static int print_f0_data(void)
-{
-	unsigned char data[0x10];
-	int r;
-	unsigned int i;
-
-	r = libusb_control_transfer(devh, CTRL_IN, USB_RQ, 0xf0, 0, data,
-		sizeof(data), 0);
-	if (r < 0) {
-		fprintf(stderr, "F0 error %d\n", r);
-		return r;
-	}
-	if ((unsigned int) r < sizeof(data)) {
-		fprintf(stderr, "short read (%d)\n", r);
-		return -1;
-	}
-
-	printf("F0 data:");
-	for (i = 0; i < sizeof(data); i++)
-		printf("%02x ", data[i]);
-	printf("\n");
-	return 0;
-}
-
-static int get_hwstat(unsigned char *status)
-{
-	int r;
-
-	r = libusb_control_transfer(devh, CTRL_IN, USB_RQ, 0x07, 0, status, 1, 0);
-	if (r < 0) {
-		fprintf(stderr, "read hwstat error %d\n", r);
-		return r;
-	}
-	if ((unsigned int) r < 1) {
-		fprintf(stderr, "short read (%d)\n", r);
-		return -1;
-	}
-
-	printf("hwstat reads %02x\n", *status);
-	return 0;
-}
-
-static int set_hwstat(unsigned char data)
-{
-	int r;
-
-	printf("set hwstat to %02x\n", data);
-	r = libusb_control_transfer(devh, CTRL_OUT, USB_RQ, 0x07, 0, &data, 1, 0);
-	if (r < 0) {
-		fprintf(stderr, "set hwstat error %d\n", r);
-		return r;
-	}
-	if ((unsigned int) r < 1) {
-		fprintf(stderr, "short write (%d)", r);
-		return -1;
-	}
-
-	return 0;
-}
-
-static int set_mode(unsigned char data)
-{
-	int r;
-	printf("set mode %02x\n", data);
-
-	r = libusb_control_transfer(devh, CTRL_OUT, USB_RQ, 0x4e, 0, &data, 1, 0);
-	if (r < 0) {
-		fprintf(stderr, "set mode error %d\n", r);
-		return r;
-	}
-	if ((unsigned int) r < 1) {
-		fprintf(stderr, "short write (%d)", r);
-		return -1;
-	}
-
-	return 0;
-}
-
-static void LIBUSB_CALL cb_mode_changed(struct libusb_transfer *transfer)
-{
-	if (transfer->status != LIBUSB_TRANSFER_COMPLETED) {
-		fprintf(stderr, "mode change transfer not completed!\n");
-		request_exit(2);
-	}
-
-	printf("async cb_mode_changed length=%d actual_length=%d\n",
-		transfer->length, transfer->actual_length);
-	if (next_state() < 0)
-		request_exit(2);
-}
-
-static int set_mode_async(unsigned char data)
-{
-	unsigned char *buf = malloc(LIBUSB_CONTROL_SETUP_SIZE + 1);
-	struct libusb_transfer *transfer;
-
-	if (!buf)
-		return -ENOMEM;
-
-	transfer = libusb_alloc_transfer(0);
-	if (!transfer) {
-		free(buf);
-		return -ENOMEM;
-	}
-
-	printf("async set mode %02x\n", data);
-	libusb_fill_control_setup(buf, CTRL_OUT, USB_RQ, 0x4e, 0, 1);
-	buf[LIBUSB_CONTROL_SETUP_SIZE] = data;
-	libusb_fill_control_transfer(transfer, devh, buf, cb_mode_changed, NULL,
-		1000);
-
-	transfer->flags = LIBUSB_TRANSFER_SHORT_NOT_OK
-		| LIBUSB_TRANSFER_FREE_BUFFER | LIBUSB_TRANSFER_FREE_TRANSFER;
-	return libusb_submit_transfer(transfer);
-}
-
-static int do_sync_intr(unsigned char *data)
-{
-	int r;
-	int transferred;
-
-	r = libusb_interrupt_transfer(devh, EP_INTR, data, INTR_LENGTH,
-		&transferred, 1000);
-	if (r < 0) {
-		fprintf(stderr, "intr error %d\n", r);
-		return r;
-	}
-	if (transferred < INTR_LENGTH) {
-		fprintf(stderr, "short read (%d)\n", r);
-		return -1;
-	}
-
-	printf("recv interrupt %04x\n", *((uint16_t *) data));
-	return 0;
-}
-
-static int sync_intr(unsigned char type)
-{
-	int r;
-	unsigned char data[INTR_LENGTH];
-
-	while (1) {
-		r = do_sync_intr(data);
-		if (r < 0)
-			return r;
-		if (data[0] == type)
-			return 0;
-	}
-}
-
-static int save_to_file(unsigned char *data)
-{
-	FILE *fd;
-	char filename[64];
-
-	snprintf(filename, sizeof(filename), "finger%d.pgm", img_idx++);
-	fd = fopen(filename, "w");
-	if (!fd)
-		return -1;
-
-	fputs("P5 384 289 255 ", fd);
-	(void) fwrite(data + 64, 1, 384*289, fd);
-	fclose(fd);
-	printf("saved image to %s\n", filename);
-	return 0;
-}
-
-static int next_state(void)
-{
-	int r = 0;
-	printf("old state: %d\n", state);
-	switch (state) {
-	case STATE_AWAIT_IRQ_FINGER_REMOVED:
-		state = STATE_AWAIT_MODE_CHANGE_AWAIT_FINGER_ON;
-		r = set_mode_async(MODE_AWAIT_FINGER_ON);
-		break;
-	case STATE_AWAIT_MODE_CHANGE_AWAIT_FINGER_ON:
-		state = STATE_AWAIT_IRQ_FINGER_DETECTED;
-		break;
-	case STATE_AWAIT_IRQ_FINGER_DETECTED:
-		state = STATE_AWAIT_MODE_CHANGE_CAPTURE;
-		r = set_mode_async(MODE_CAPTURE);
-		break;
-	case STATE_AWAIT_MODE_CHANGE_CAPTURE:
-		state = STATE_AWAIT_IMAGE;
-		break;
-	case STATE_AWAIT_IMAGE:
-		state = STATE_AWAIT_MODE_CHANGE_AWAIT_FINGER_OFF;
-		r = set_mode_async(MODE_AWAIT_FINGER_OFF);
-		break;
-	case STATE_AWAIT_MODE_CHANGE_AWAIT_FINGER_OFF:
-		state = STATE_AWAIT_IRQ_FINGER_REMOVED;
-		break;
-	default:
-		printf("unrecognised state %d\n", state);
-	}
-	if (r < 0) {
-		fprintf(stderr, "error detected changing state\n");
-		return r;
-	}
-
-	printf("new state: %d\n", state);
-	return 0;
-}
-
-static void LIBUSB_CALL cb_irq(struct libusb_transfer *transfer)
-{
-	unsigned char irqtype = transfer->buffer[0];
-
-	if (transfer->status != LIBUSB_TRANSFER_COMPLETED) {
-		fprintf(stderr, "irq transfer status %d?\n", transfer->status);
-		irq_transfer = NULL;
-		request_exit(2);
-		return;
-	}
-
-	printf("IRQ callback %02x\n", irqtype);
-	switch (state) {
-	case STATE_AWAIT_IRQ_FINGER_DETECTED:
-		if (irqtype == 0x01) {
-			if (next_state() < 0) {
-				request_exit(2);
-				return;
-			}
-		} else {
-			printf("finger-on-sensor detected in wrong state!\n");
-		}
-		break;
-	case STATE_AWAIT_IRQ_FINGER_REMOVED:
-		if (irqtype == 0x02) {
-			if (next_state() < 0) {
-				request_exit(2);
-				return;
-			}
-		} else {
-			printf("finger-on-sensor detected in wrong state!\n");
-		}
-		break;
-	}
-	if (libusb_submit_transfer(irq_transfer) < 0)
-		request_exit(2);
-}
-
-static void LIBUSB_CALL cb_img(struct libusb_transfer *transfer)
-{
-	if (transfer->status != LIBUSB_TRANSFER_COMPLETED) {
-		fprintf(stderr, "img transfer status %d?\n", transfer->status);
-		img_transfer = NULL;
-		request_exit(2);
-		return;
-	}
-
-	printf("Image callback\n");
-	save_to_file(imgbuf);
-	if (next_state() < 0) {
-		request_exit(2);
-		return;
-	}
-	if (libusb_submit_transfer(img_transfer) < 0)
-		request_exit(2);
-}
-
-static int init_capture(void)
-{
-	int r;
-
-	r = libusb_submit_transfer(irq_transfer);
-	if (r < 0)
-		return r;
-
-	r = libusb_submit_transfer(img_transfer);
-	if (r < 0) {
-		libusb_cancel_transfer(irq_transfer);
-		while (irq_transfer)
-			if (libusb_handle_events(NULL) < 0)
-				break;
-		return r;
-	}
-
-	/* start state machine */
-	state = STATE_AWAIT_IRQ_FINGER_REMOVED;
-	return next_state();
-}
-
-static int do_init(void)
-{
-	unsigned char status;
-	int r;
-
-	r = get_hwstat(&status);
-	if (r < 0)
-		return r;
-
-	if (!(status & 0x80)) {
-		r = set_hwstat(status | 0x80);
-		if (r < 0)
-			return r;
-		r = get_hwstat(&status);
-		if (r < 0)
-			return r;
-	}
-
-	status &= ~0x80;
-	r = set_hwstat(status);
-	if (r < 0)
-		return r;
-
-	r = get_hwstat(&status);
-	if (r < 0)
-		return r;
-
-	r = sync_intr(0x56);
-	if (r < 0)
-		return r;
-
-	return 0;
-}
-
-static int alloc_transfers(void)
-{
-	img_transfer = libusb_alloc_transfer(0);
-	if (!img_transfer)
-		return -ENOMEM;
-
-	irq_transfer = libusb_alloc_transfer(0);
-	if (!irq_transfer)
-		return -ENOMEM;
-
-	libusb_fill_bulk_transfer(img_transfer, devh, EP_DATA, imgbuf,
-		sizeof(imgbuf), cb_img, NULL, 0);
-	libusb_fill_interrupt_transfer(irq_transfer, devh, EP_INTR, irqbuf,
-		sizeof(irqbuf), cb_irq, NULL, 0);
-
-	return 0;
-}
-
-static void sighandler(int signum)
-{
-	request_exit(1);
-}
-
-int main(void)
-{
-	struct sigaction sigact;
-	int r = 1;
-
-	r = libusb_init(NULL);
-	if (r < 0) {
-		fprintf(stderr, "failed to initialise libusb\n");
-		exit(1);
-	}
-
-	r = find_dpfp_device();
-	if (r < 0) {
-		fprintf(stderr, "Could not find/open device\n");
-		goto out;
-	}
-
-	r = libusb_claim_interface(devh, 0);
-	if (r < 0) {
-		fprintf(stderr, "usb_claim_interface error %d %s\n", r, strerror(-r));
-		goto out;
-	}
-	printf("claimed interface\n");
-
-	r = print_f0_data();
-	if (r < 0)
-		goto out_release;
-
-	r = do_init();
-	if (r < 0)
-		goto out_deinit;
-
-	/* async from here onwards */
-
-	sigact.sa_handler = sighandler;
-	sigemptyset(&sigact.sa_mask);
-	sigact.sa_flags = 0;
-	sigaction(SIGINT, &sigact, NULL);
-	sigaction(SIGTERM, &sigact, NULL);
-	sigaction(SIGQUIT, &sigact, NULL);
-
-	r = pthread_create(&poll_thread, NULL, poll_thread_main, NULL);
-	if (r)
-		goto out_deinit;
-
-	r = alloc_transfers();
-	if (r < 0) {
-		request_exit(1);
-		pthread_join(poll_thread, NULL);
-		goto out_deinit;
-	}
-
-	r = init_capture();
-	if (r < 0) {
-		request_exit(1);
-		pthread_join(poll_thread, NULL);
-		goto out_deinit;
-	}
-
-	while (!do_exit) {
-		pthread_mutex_lock(&exit_cond_lock);
-		pthread_cond_wait(&exit_cond, &exit_cond_lock);
-		pthread_mutex_unlock(&exit_cond_lock);
-	}
-
-	printf("shutting down...\n");
-	pthread_join(poll_thread, NULL);
-
-	r = libusb_cancel_transfer(irq_transfer);
-	if (r < 0) {
-		request_exit(1);
-		goto out_deinit;
-	}
-
-	r = libusb_cancel_transfer(img_transfer);
-	if (r < 0) {
-		request_exit(1);
-		goto out_deinit;
-	}
-
-	while (img_transfer || irq_transfer)
-		if (libusb_handle_events(NULL) < 0)
-			break;
-
-	if (do_exit == 1)
-		r = 0;
-	else
-		r = 1;
-
-out_deinit:
-	libusb_free_transfer(img_transfer);
-	libusb_free_transfer(irq_transfer);
-	set_mode(0);
-	set_hwstat(0x80);
-out_release:
-	libusb_release_interface(devh, 0);
-out:
-	libusb_close(devh);
-	libusb_exit(NULL);
-	return r >= 0 ? r : -r;
-}
-

+ 0 - 95
compat/libusb-1.0/examples/hotplugtest.c

@@ -1,95 +0,0 @@
-/*
- * libusb example program for hotplug API
- * Copyright (C) 2012-2013 Nathan Hjelm <hjelmn@mac.ccom>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-#include <stdlib.h>
-#include <stdio.h>
-#include <sched.h>
-#include <unistd.h>
-
-#include <libusb.h>
-
-int done = 0;
-libusb_device_handle *handle;
-
-static int hotplug_callback (libusb_context *ctx, libusb_device *dev, libusb_hotplug_event event, void *user_data) {
-  struct libusb_device_descriptor desc;
-  int rc;
-
-  rc = libusb_get_device_descriptor(dev, &desc);
-  if (LIBUSB_SUCCESS != rc) {
-    fprintf (stderr, "Error getting device descriptor\n");
-  }
-
-  printf ("Device attach: %04x:%04x\n", desc.idVendor, desc.idProduct);
-
-  libusb_open (dev, &handle);
-
-  done++;
-
-  return 0;
-}
-
-static int hotplug_callback_detach (libusb_context *ctx, libusb_device *dev, libusb_hotplug_event event, void *user_data) {
-  printf ("Device detached\n");
-
-  libusb_close (handle);
-
-  done++;
-  return 0;
-}
-
-int main (int argc, char *argv[]) {
-  libusb_hotplug_callback_handle hp[2];
-  int product_id, vendor_id, class_id;
-  int rc;
-
-  vendor_id  = (argc > 1) ? strtol (argv[1], NULL, 0) : 0x045a;
-  product_id = (argc > 2) ? strtol (argv[2], NULL, 0) : 0x5005;
-  class_id   = (argc > 3) ? strtol (argv[3], NULL, 0) : LIBUSB_HOTPLUG_MATCH_ANY;
-
-  libusb_init (NULL);
-
-  if (!libusb_has_capability (LIBUSB_CAP_HAS_HOTPLUG)) {
-    printf ("Hotplug not supported by this build of libusb\n");
-    libusb_exit (NULL);
-    return EXIT_FAILURE;
-  }
-
-  rc = libusb_hotplug_register_callback (NULL, LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED, 0, vendor_id,
-                                         product_id, class_id, hotplug_callback, NULL, &hp[0]);
-  if (LIBUSB_SUCCESS != rc) {
-    fprintf (stderr, "Error registering callback 0\n");
-    libusb_exit (NULL);
-    return EXIT_FAILURE;
-  }
-
-  rc = libusb_hotplug_register_callback (NULL, LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT, 0, vendor_id,
-                                         product_id,class_id, hotplug_callback_detach, NULL, &hp[1]);
-  if (LIBUSB_SUCCESS != rc) {
-    fprintf (stderr, "Error registering callback 1\n");
-    libusb_exit (NULL);
-    return EXIT_FAILURE;
-  }
-
-  while (done < 2) {
-    libusb_handle_events (NULL);
-  }
-
-  libusb_exit (NULL);
-}

+ 0 - 64
compat/libusb-1.0/examples/listdevs.c

@@ -1,64 +0,0 @@
-/*
- * libusb example program to list devices on the bus
- * Copyright (C) 2007 Daniel Drake <dsd@gentoo.org>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-#include <stdio.h>
-#include <sys/types.h>
-
-#include <libusb.h>
-
-static void print_devs(libusb_device **devs)
-{
-	libusb_device *dev;
-	int i = 0;
-
-	while ((dev = devs[i++]) != NULL) {
-		struct libusb_device_descriptor desc;
-		int r = libusb_get_device_descriptor(dev, &desc);
-		if (r < 0) {
-			fprintf(stderr, "failed to get device descriptor");
-			return;
-		}
-
-		printf("%04x:%04x (bus %d, device %d)\n",
-			desc.idVendor, desc.idProduct,
-			libusb_get_bus_number(dev), libusb_get_device_address(dev));
-	}
-}
-
-int main(void)
-{
-	libusb_device **devs;
-	int r;
-	ssize_t cnt;
-
-	r = libusb_init(NULL);
-	if (r < 0)
-		return r;
-
-	cnt = libusb_get_device_list(NULL, &devs);
-	if (cnt < 0)
-		return (int) cnt;
-
-	print_devs(devs);
-	libusb_free_device_list(devs, 1);
-
-	libusb_exit(NULL);
-	return 0;
-}
-

+ 0 - 193
compat/libusb-1.0/examples/sam3u_benchmark.c

@@ -1,193 +0,0 @@
-/*
- * libusb example program to measure Atmel SAM3U isochronous performance
- * Copyright (C) 2012 Harald Welte <laforge@gnumonks.org>
- *
- * Copied with the author's permission under LGPL-2.1 from
- * http://git.gnumonks.org/cgi-bin/gitweb.cgi?p=sam3u-tests.git;a=blob;f=usb-benchmark-project/host/benchmark.c;h=74959f7ee88f1597286cd435f312a8ff52c56b7e
- *
- * An Atmel SAM3U test firmware is also available in the above repository.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-#include <unistd.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <errno.h>
-#include <signal.h>
-
-#include <libusb.h>
-
-
-#define EP_DATA_IN	0x82
-#define EP_ISO_IN	0x86
-
-static int do_exit = 0;
-static struct libusb_device_handle *devh = NULL;
-
-static unsigned long num_bytes = 0, num_xfer = 0;
-static struct timeval tv_start;
-
-static void cb_xfr(struct libusb_transfer *xfr)
-{
-	unsigned int i;
-
-	if (xfr->status != LIBUSB_TRANSFER_COMPLETED) {
-		fprintf(stderr, "transfer status %d\n", xfr->status);
-		libusb_free_transfer(xfr);
-		exit(3);
-	}
-
-	if (xfr->type == LIBUSB_TRANSFER_TYPE_ISOCHRONOUS) {
-		for (i = 0; i < xfr->num_iso_packets; i++) {
-			struct libusb_iso_packet_descriptor *pack = &xfr->iso_packet_desc[i];
-
-			if (pack->status != LIBUSB_TRANSFER_COMPLETED) {
-				fprintf(stderr, "Error: pack %u status %d\n", i, pack->status);
-				exit(5);
-			}
-
-			printf("pack%u length:%u, actual_length:%u\n", i, pack->length, pack->actual_length);
-		}
-	}
-
-	printf("length:%u, actual_length:%u\n", xfr->length, xfr->actual_length);
-	for (i = 0; i < xfr->actual_length; i++) {
-		printf("%02x", xfr->buffer[i]);
-		if (i % 16)
-			printf("\n");
-		else if (i % 8)
-			printf("  ");
-		else
-			printf(" ");
-	}
-	num_bytes += xfr->actual_length;
-	num_xfer++;
-
-	if (libusb_submit_transfer(xfr) < 0) {
-		fprintf(stderr, "error re-submitting URB\n");
-		exit(1);
-	}
-}
-
-static int benchmark_in(uint8_t ep)
-{
-	static uint8_t buf[2048];
-	static struct libusb_transfer *xfr;
-	int num_iso_pack = 0;
-
-	if (ep == EP_ISO_IN)
-		num_iso_pack = 16;
-
-	xfr = libusb_alloc_transfer(num_iso_pack);
-	if (!xfr)
-		return -ENOMEM;
-
-	if (ep == EP_ISO_IN) {
-		libusb_fill_iso_transfer(xfr, devh, ep, buf,
-				sizeof(buf), num_iso_pack, cb_xfr, NULL, 0);
-		libusb_set_iso_packet_lengths(xfr, sizeof(buf)/num_iso_pack);
-	} else
-		libusb_fill_bulk_transfer(xfr, devh, ep, buf,
-				sizeof(buf), cb_xfr, NULL, 0);
-
-	gettimeofday(&tv_start, NULL);
-
-	/* NOTE: To reach maximum possible performance the program must
-	 * submit *multiple* transfers here, not just one.
-	 *
-	 * When only one transfer is submitted there is a gap in the bus
-	 * schedule from when the transfer completes until a new transfer
-	 * is submitted by the callback. This causes some jitter for
-	 * isochronous transfers and loss of throughput for bulk transfers.
-	 *
-	 * This is avoided by queueing multiple transfers in advance, so
-	 * that the host controller is always kept busy, and will schedule
-	 * more transfers on the bus while the callback is running for
-	 * transfers which have completed on the bus.
-	 */
-
-	return libusb_submit_transfer(xfr);
-}
-
-static void measure(void)
-{
-	struct timeval tv_stop;
-	unsigned int diff_msec;
-
-	gettimeofday(&tv_stop, NULL);
-
-	diff_msec = (tv_stop.tv_sec - tv_start.tv_sec)*1000;
-	diff_msec += (tv_stop.tv_usec - tv_start.tv_usec)/1000;
-
-	printf("%lu transfers (total %lu bytes) in %u miliseconds => %lu bytes/sec\n",
-		num_xfer, num_bytes, diff_msec, (num_bytes*1000)/diff_msec);
-}
-
-static void sig_hdlr(int signum)
-{
-	switch (signum) {
-	case SIGINT:
-		measure();
-		do_exit = 1;
-		break;
-	}
-}
-
-int main(int argc, char **argv)
-{
-	int rc;
-	struct sigaction sigact;
-
-	sigact.sa_handler = sig_hdlr;
-	sigemptyset(&sigact.sa_mask);
-	sigact.sa_flags = 0;
-	sigaction(SIGINT, &sigact, NULL);
-
-	rc = libusb_init(NULL);
-	if (rc < 0) {
-		fprintf(stderr, "Error initializing libusb: %s\n", libusb_error_name(rc));
-		exit(1);
-	}
-
-	devh = libusb_open_device_with_vid_pid(NULL, 0x16c0, 0x0763);
-	if (!devh) {
-		fprintf(stderr, "Error finding USB device\n");
-		goto out;
-	}
-
-	rc = libusb_claim_interface(devh, 2);
-	if (rc < 0) {
-		fprintf(stderr, "Error claiming interface: %s\n", libusb_error_name(rc));
-		goto out;
-	}
-
-	benchmark_in(EP_ISO_IN);
-
-	while (!do_exit) {
-		rc = libusb_handle_events(NULL);
-		if (rc != LIBUSB_SUCCESS)
-			break;
-	}
-
-	/* Measurement has already been done by the signal handler. */
-
-	libusb_release_interface(devh, 0);
-out:
-	if (devh)
-		libusb_close(devh);
-	libusb_exit(NULL);
-	return rc;
-}

+ 0 - 256
compat/libusb-1.0/examples/testlibusb1.c

@@ -1,256 +0,0 @@
-/*
- * Test suite program based of libusb-0.1-compat testlibusb
- * Copyright (c) 2013 Nathan Hjelm <hjelmn@mac.ccom>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-#include <stdio.h>
-#include <string.h>
-#include <libusb.h>
-
-int verbose = 0;
-
-static void print_endpoint_comp(const struct libusb_ss_endpoint_companion_descriptor *ep_comp)
-{
-  printf("      USB 3.0 Endpoint Companion:\n");
-  printf("        bMaxBurst:        %d\n", ep_comp->bMaxBurst);
-  printf("        bmAttributes:     0x%02x\n", ep_comp->bmAttributes);
-  printf("        wBytesPerInterval: %d\n", ep_comp->wBytesPerInterval);
-}
-
-static void print_endpoint(const struct libusb_endpoint_descriptor *endpoint)
-{
-  int i, ret;
-
-  printf("      Endpoint:\n");
-  printf("        bEndpointAddress: %02xh\n", endpoint->bEndpointAddress);
-  printf("        bmAttributes:     %02xh\n", endpoint->bmAttributes);
-  printf("        wMaxPacketSize:   %d\n", endpoint->wMaxPacketSize);
-  printf("        bInterval:        %d\n", endpoint->bInterval);
-  printf("        bRefresh:         %d\n", endpoint->bRefresh);
-  printf("        bSynchAddress:    %d\n", endpoint->bSynchAddress);
-
-  for (i = 0 ; i < endpoint->extra_length ; ) {
-    if (LIBUSB_DT_SS_ENDPOINT_COMPANION == endpoint->extra[i+1]) {
-      struct libusb_ss_endpoint_companion_descriptor *ep_comp;
-
-      ret = libusb_parse_ss_endpoint_comp(endpoint->extra+i, endpoint->extra[0], &ep_comp);
-      if (LIBUSB_SUCCESS != ret) {
-        continue;
-      }
-
-      print_endpoint_comp(ep_comp);
-
-      libusb_free_ss_endpoint_comp(ep_comp);
-    }
-
-    i += endpoint->extra[i];
-  }
-}
-
-static void print_altsetting(const struct libusb_interface_descriptor *interface)
-{
-  int i;
-
-  printf("    Interface:\n");
-  printf("      bInterfaceNumber:   %d\n", interface->bInterfaceNumber);
-  printf("      bAlternateSetting:  %d\n", interface->bAlternateSetting);
-  printf("      bNumEndpoints:      %d\n", interface->bNumEndpoints);
-  printf("      bInterfaceClass:    %d\n", interface->bInterfaceClass);
-  printf("      bInterfaceSubClass: %d\n", interface->bInterfaceSubClass);
-  printf("      bInterfaceProtocol: %d\n", interface->bInterfaceProtocol);
-  printf("      iInterface:         %d\n", interface->iInterface);
-
-  for (i = 0; i < interface->bNumEndpoints; i++)
-    print_endpoint(&interface->endpoint[i]);
-}
-
-static void print_2_0_ext_cap(struct libusb_usb_2_0_device_capability_descriptor *usb_2_0_ext_cap)
-{
-  printf("    USB 2.0 Extension Capabilities:\n");
-  printf("      bDevCapabilityType: %d\n", usb_2_0_ext_cap->bDevCapabilityType);
-  printf("      bmAttributes:       0x%x\n", usb_2_0_ext_cap->bmAttributes);
-}
-
-static void print_ss_usb_cap(struct libusb_ss_usb_device_capability_descriptor *ss_usb_cap)
-{
-  printf("    USB 3.0 Capabilities:\n");
-  printf("      bDevCapabilityType: %d\n", ss_usb_cap->bDevCapabilityType);
-  printf("      bmAttributes:       0x%x\n", ss_usb_cap->bmAttributes);
-  printf("      wSpeedSupported:    0x%x\n", ss_usb_cap->wSpeedSupported);
-  printf("      bFunctionalitySupport: %d\n", ss_usb_cap->bFunctionalitySupport);
-  printf("      bU1devExitLat:      %d\n", ss_usb_cap->bU1DevExitLat);
-  printf("      bU2devExitLat:      %d\n", ss_usb_cap->bU2DevExitLat);
-}
-
-static void print_bos(libusb_device_handle *handle)
-{
-  unsigned char buffer[128];
-  struct libusb_bos_descriptor *bos;
-  int ret;
-
-  ret = libusb_get_descriptor(handle, LIBUSB_DT_BOS, 0, buffer, 128);
-  if (0 > ret) {
-    return;
-  }
-
-  ret = libusb_parse_bos_descriptor(buffer, 128, &bos);
-  if (0 > ret) {
-    return;
-  }
-
-  printf("  Binary Object Store (BOS):\n");
-  printf("    wTotalLength:       %d\n", bos->wTotalLength);
-  printf("    bNumDeviceCaps:     %d\n", bos->bNumDeviceCaps);
-  if (bos->usb_2_0_ext_cap) {
-    print_2_0_ext_cap(bos->usb_2_0_ext_cap);
-  }
-
-  if (bos->ss_usb_cap) {
-    print_ss_usb_cap(bos->ss_usb_cap);
-  }
-}
-
-static void print_interface(const struct libusb_interface *interface)
-{
-  int i;
-
-  for (i = 0; i < interface->num_altsetting; i++)
-    print_altsetting(&interface->altsetting[i]);
-}
-
-static void print_configuration(struct libusb_config_descriptor *config)
-{
-  int i;
-
-  printf("  Configuration:\n");
-  printf("    wTotalLength:         %d\n", config->wTotalLength);
-  printf("    bNumInterfaces:       %d\n", config->bNumInterfaces);
-  printf("    bConfigurationValue:  %d\n", config->bConfigurationValue);
-  printf("    iConfiguration:       %d\n", config->iConfiguration);
-  printf("    bmAttributes:         %02xh\n", config->bmAttributes);
-  printf("    MaxPower:             %d\n", config->MaxPower);
-
-  for (i = 0; i < config->bNumInterfaces; i++)
-    print_interface(&config->interface[i]);
-}
-
-static int print_device(libusb_device *dev, int level)
-{
-  struct libusb_device_descriptor desc;
-  libusb_device_handle *handle = NULL;
-  char description[256];
-  char string[256];
-  int ret, i;
-
-  ret = libusb_get_device_descriptor(dev, &desc);
-  if (ret < 0) {
-    fprintf(stderr, "failed to get device descriptor");
-    return -1;
-  }
-
-  ret = libusb_open(dev, &handle);
-  if (LIBUSB_SUCCESS == ret) {
-    if (desc.iManufacturer) {
-      ret = libusb_get_string_descriptor_ascii(handle, desc.iManufacturer, string, sizeof(string));
-      if (ret > 0)
-        snprintf(description, sizeof(description), "%s - ", string);
-      else
-        snprintf(description, sizeof(description), "%04X - ",
-                 desc.idVendor);
-    } else
-      snprintf(description, sizeof(description), "%04X - ",
-               desc.idVendor);
-
-    if (desc.iProduct) {
-      ret = libusb_get_string_descriptor_ascii(handle, desc.iProduct, string, sizeof(string));
-      if (ret > 0)
-        snprintf(description + strlen(description), sizeof(description) -
-                 strlen(description), "%s", string);
-      else
-        snprintf(description + strlen(description), sizeof(description) -
-                 strlen(description), "%04X", desc.idProduct);
-    } else
-      snprintf(description + strlen(description), sizeof(description) -
-               strlen(description), "%04X", desc.idProduct);
-  } else {
-    snprintf(description, sizeof(description), "%04X - %04X",
-             desc.idVendor, desc.idProduct);
-  }
-
-  printf("%.*sDev (bus %d, device %d): %s\n", level * 2, "                    ",
-         libusb_get_bus_number(dev), libusb_get_device_address(dev), description);
-
-  if (handle && verbose) {
-    if (desc.iSerialNumber) {
-      ret = libusb_get_string_descriptor_ascii(handle, desc.iSerialNumber, string, sizeof(string));
-      if (ret > 0)
-        printf("%.*s  - Serial Number: %s\n", level * 2,
-               "                    ", string);
-    }
-  }
-
-  if (verbose) {
-    for (i = 0; i < desc.bNumConfigurations; i++) {
-      struct libusb_config_descriptor *config;
-      ret = libusb_get_config_descriptor(dev, i, &config);
-      if (LIBUSB_SUCCESS != ret) {
-        printf("  Couldn't retrieve descriptors\n");
-        continue;
-      }
-
-      print_configuration(config);
-
-      libusb_free_config_descriptor(config);
-    }
-
-    if (handle && desc.bcdUSB >= 0x0201) {
-      print_bos(handle);
-    }
-  }
-
-  if (handle)
-    libusb_close(handle);
-
-  return 0;
-}
-
-int main(int argc, char *argv[])
-{
-  libusb_device **devs;
-  ssize_t cnt;
-  int r, i;
-
-  if (argc > 1 && !strcmp(argv[1], "-v"))
-    verbose = 1;
-
-  r = libusb_init(NULL);
-  if (r < 0)
-    return r;
-
-  cnt = libusb_get_device_list(NULL, &devs);
-  if (cnt < 0)
-    return (int) cnt;
-
-  for (i = 0 ; devs[i] ; ++i) {
-    print_device(devs[i], 0);
-  }
-
-  libusb_free_device_list(devs, 1);
-
-  libusb_exit(NULL);
-  return 0;
-}

+ 4 - 4
configure.ac

@@ -1,8 +1,8 @@
 ##--##--##--##--##--##--##--##--##--##--##--##--##--##--##--##--##
 ##--##--##--##--##--##--##--##--##--##--##--##--##--##--##--##--##
 m4_define([v_maj], [3])
-m4_define([v_min], [5])
-m4_define([v_mic], [0])
+m4_define([v_min], [6])
+m4_define([v_mic], [3])
 ##--##--##--##--##--##--##--##--##--##--##--##--##--##--##--##--##
 m4_define([v_ver], [v_maj.v_min.v_mic])
 m4_define([lt_rev], m4_eval(v_maj + v_min))
@@ -359,7 +359,8 @@ else
 	LIBUSB_LIBS=""
 fi
 
-JANSSON_LIBS="compat/jansson/libjansson.a"
+AC_CONFIG_SUBDIRS([compat/jansson-2.5])
+JANSSON_LIBS="compat/jansson-2.5/src/.libs/libjansson.a"
 
 PKG_PROG_PKG_CONFIG()
 
@@ -466,7 +467,6 @@ AC_SUBST(ADL_CPPFLAGS)
 AC_CONFIG_FILES([
 	Makefile
 	compat/Makefile
-	compat/jansson/Makefile
 	ccan/Makefile
 	lib/Makefile
 	])

+ 80 - 11
driver-avalon.c

@@ -49,6 +49,7 @@ int opt_avalon_fan_max = AVALON_DEFAULT_FAN_MAX_PWM;
 int opt_avalon_freq_min = AVALON_MIN_FREQUENCY;
 int opt_avalon_freq_max = AVALON_MAX_FREQUENCY;
 int opt_bitburner_core_voltage = BITBURNER_DEFAULT_CORE_VOLTAGE;
+int opt_bitburner_fury_core_voltage = BITBURNER_FURY_DEFAULT_CORE_VOLTAGE;
 bool opt_avalon_auto;
 
 static int option_offset = -1;
@@ -209,6 +210,55 @@ static int avalon_send_task(const struct avalon_task *at, struct cgpu_info *aval
 	return ret;
 }
 
+static int bitburner_send_task(const struct avalon_task *at, struct cgpu_info *avalon)
+
+{
+	uint8_t buf[AVALON_WRITE_SIZE + 4 * AVALON_DEFAULT_ASIC_NUM];
+	int ret, ep = C_AVALON_TASK;
+	cgtimer_t ts_start;
+	size_t nr_len;
+
+	if (at->nonce_elf)
+		nr_len = AVALON_WRITE_SIZE + 4 * at->asic_num;
+	else
+		nr_len = AVALON_WRITE_SIZE;
+
+	memset(buf, 0, nr_len);
+	memcpy(buf, at, AVALON_WRITE_SIZE);
+
+#if defined(__BIG_ENDIAN__) || defined(MIPSEB)
+	uint8_t tt = 0;
+
+	tt = (buf[0] & 0x0f) << 4;
+	tt |= ((buf[0] & 0x10) ? (1 << 3) : 0);
+	tt |= ((buf[0] & 0x20) ? (1 << 2) : 0);
+	tt |= ((buf[0] & 0x40) ? (1 << 1) : 0);
+	tt |= ((buf[0] & 0x80) ? (1 << 0) : 0);
+	buf[0] = tt;
+
+	tt = (buf[4] & 0x0f) << 4;
+	tt |= ((buf[4] & 0x10) ? (1 << 3) : 0);
+	tt |= ((buf[4] & 0x20) ? (1 << 2) : 0);
+	tt |= ((buf[4] & 0x40) ? (1 << 1) : 0);
+	tt |= ((buf[4] & 0x80) ? (1 << 0) : 0);
+	buf[4] = tt;
+#endif
+
+	if (at->reset) {
+		ep = C_AVALON_RESET;
+		nr_len = 1;
+	}
+	if (opt_debug) {
+		applog(LOG_DEBUG, "Avalon: Sent(%u):", (unsigned int)nr_len);
+		hexdump(buf, nr_len);
+	}
+	cgsleep_prepare_r(&ts_start);
+	ret = avalon_write(avalon, (char *)buf, nr_len, ep);
+	cgsleep_us_r(&ts_start, 3000); // 3 ms = 333 tasks per second, or 1.4 TH/s
+
+	return ret;
+}
+
 static bool avalon_decode_nonce(struct thr_info *thr, struct cgpu_info *avalon,
 				struct avalon_info *info, struct avalon_result *ar,
 				struct work *work)
@@ -414,12 +464,12 @@ static bool get_options(int this_option_offset, int *baud, int *miner_count,
 
 		if (*colon) {
 			tmp = atoi(colon);
-			if (tmp > 0 && tmp <= AVALON_DEFAULT_MINER_NUM) {
+			if (tmp > 0 && tmp <= AVALON_MAX_MINER_NUM) {
 				*miner_count = tmp;
 			} else {
 				quit(1, "Invalid avalon-options for "
 					"miner_count (%s) must be 1 ~ %d",
-					colon, AVALON_DEFAULT_MINER_NUM);
+					colon, AVALON_MAX_MINER_NUM);
 			}
 		}
 
@@ -620,12 +670,20 @@ static void avalon_initialise(struct cgpu_info *avalon)
 		avalon->drv->name, avalon->device_id, err);
 }
 
+static bool is_bitburner(struct cgpu_info *avalon)
+{
+	enum sub_ident ident;
+
+	ident = usb_ident(avalon);
+	return ident == IDENT_BTB || ident == IDENT_BBF;
+}
+
 static bool bitburner_set_core_voltage(struct cgpu_info *avalon, int core_voltage)
 {
 	uint8_t buf[2];
 	int err;
 
-	if (usb_ident(avalon) == IDENT_BTB) {
+	if (is_bitburner(avalon)) {
 		buf[0] = (uint8_t)core_voltage;
 		buf[1] = (uint8_t)(core_voltage >> 8);
 		err = usb_transfer_data(avalon, FTDI_TYPE_OUT, BITBURNER_REQUEST,
@@ -651,7 +709,7 @@ static int bitburner_get_core_voltage(struct cgpu_info *avalon)
 	int err;
 	int amount;
 
-	if (usb_ident(avalon) == IDENT_BTB) {
+	if (is_bitburner(avalon)) {
 		err = usb_transfer_read(avalon, FTDI_TYPE_IN, BITBURNER_REQUEST,
 				BITBURNER_VALUE, BITBURNER_INDEX_GET_VOLTAGE,
 				(char *)buf, sizeof(buf), &amount,
@@ -780,7 +838,18 @@ static bool avalon_detect_one(libusb_device *dev, struct usb_find_devices *found
 				BITBURNER_MAX_COREMV);
 		} else
 			bitburner_set_core_voltage(avalon, opt_bitburner_core_voltage);
+	} else if (usb_ident(avalon) == IDENT_BBF) {
+		if (opt_bitburner_fury_core_voltage < BITBURNER_FURY_MIN_COREMV ||
+		    opt_bitburner_fury_core_voltage > BITBURNER_FURY_MAX_COREMV) {
+			quit(1, "Invalid bitburner-fury-voltage %d must be %dmv - %dmv",
+				opt_bitburner_fury_core_voltage,
+				BITBURNER_FURY_MIN_COREMV,
+				BITBURNER_FURY_MAX_COREMV);
+		} else
+			bitburner_set_core_voltage(avalon, opt_bitburner_fury_core_voltage);
+	}
 
+	if (is_bitburner(avalon)) {
 		bitburner_get_version(avalon);
 	}
 
@@ -1181,7 +1250,7 @@ static void *bitburner_send_tasks(void *userdata)
 				avalon_reset_auto(info);
 			}
 
-			ret = avalon_send_task(&at, avalon);
+			ret = bitburner_send_task(&at, avalon);
 
 			if (unlikely(ret == AVA_SEND_ERROR)) {
 				applog(LOG_ERR, "%s%i: Comms error(buffer)",
@@ -1211,7 +1280,7 @@ static bool avalon_prepare(struct thr_info *thr)
 	int array_size = AVALON_ARRAY_SIZE;
 	void *(*write_thread_fn)(void *) = avalon_send_tasks;
 
-	if (usb_ident(avalon) == IDENT_BTB) {
+	if (is_bitburner(avalon)) {
 		array_size = BITBURNER_ARRAY_SIZE;
 		write_thread_fn = bitburner_send_tasks;
 	}
@@ -1356,7 +1425,7 @@ static void avalon_update_temps(struct cgpu_info *avalon, struct avalon_info *in
 	info->temp_sum += avalon->temp;
 	applog(LOG_DEBUG, "Avalon: temp_index: %d, temp_count: %d, temp_old: %d",
 		info->temp_history_index, info->temp_history_count, info->temp_old);
-	if (usb_ident(avalon) == IDENT_BTB) {
+	if (is_bitburner(avalon)) {
 		info->core_voltage = bitburner_get_core_voltage(avalon);
 	}
 	if (info->temp_history_index == info->temp_history_count) {
@@ -1378,7 +1447,7 @@ static void get_avalon_statline_before(char *buf, size_t bufsiz, struct cgpu_inf
 	struct avalon_info *info = avalon->device_data;
 	int lowfan = 10000;
 
-	if (usb_ident(avalon) == IDENT_BTB) {
+	if (is_bitburner(avalon)) {
 		int temp = info->temp0;
 		if (info->temp2 > temp)
 			temp = info->temp2;
@@ -1464,7 +1533,7 @@ static int64_t avalon_scanhash(struct thr_info *thr)
 
 	/* Check for nothing but consecutive bad results or consistently less
 	 * results than we should be getting and reset the FPGA if necessary */
-	if (usb_ident(avalon) != IDENT_BTB) {
+	if (!is_bitburner(avalon)) {
 		if (avalon->results < -miner_count && !info->reset) {
 			applog(LOG_ERR, "%s%d: Result return rate low, resetting!",
 				avalon->drv->name, avalon->device_id);
@@ -1525,7 +1594,7 @@ static struct api_data *avalon_api_stats(struct cgpu_info *cgpu)
 		sprintf(mcw, "match_work_count%d", i + 1);
 		root = api_add_int(root, mcw, &(info->matching_work[i]), false);
 	}
-	if (usb_ident(cgpu) == IDENT_BTB) {
+	if (is_bitburner(cgpu)) {
 		root = api_add_int(root, "core_voltage", &(info->core_voltage), false);
 		snprintf(buf, sizeof(buf), "%"PRIu8".%"PRIu8".%"PRIu8,
 				info->version1, info->version2, info->version3);
@@ -1553,7 +1622,7 @@ static char *avalon_set_device(struct cgpu_info *avalon, char *option, char *set
 	}
 
 	if (strcasecmp(option, "millivolts") == 0 || strcasecmp(option, "mv") == 0) {
-		if (usb_ident(avalon) != IDENT_BTB) {
+		if (!is_bitburner(avalon)) {
 			sprintf(replybuf, "%s cannot set millivolts", avalon->drv->name);
 			return replybuf;
 		}

+ 10 - 1
driver-avalon.h

@@ -33,11 +33,18 @@
 #define AVALON_TEMP_HYSTERESIS 3
 #define AVALON_TEMP_OVERHEAT 60
 
+/* Avalon-based BitBurner. */
 #define BITBURNER_DEFAULT_CORE_VOLTAGE 1200 /* in millivolts */
 #define BITBURNER_MIN_COREMV 1000
 /* change here if you want to risk killing it :)  */
 #define BITBURNER_MAX_COREMV 1400
 
+/* BitFury-based BitBurner. */
+#define BITBURNER_FURY_DEFAULT_CORE_VOLTAGE 900 /* in millivolts */
+#define BITBURNER_FURY_MIN_COREMV 700
+/* change here if you want to risk killing it :)  */
+#define BITBURNER_FURY_MAX_COREMV 1100
+
 
 #define AVALON_DEFAULT_TIMEOUT 0x2D
 #define AVALON_MIN_FREQUENCY 256
@@ -45,6 +52,7 @@
 #define AVALON_TIMEOUT_FACTOR 12690
 #define AVALON_DEFAULT_FREQUENCY 282
 #define AVALON_DEFAULT_MINER_NUM 0x20
+#define AVALON_MAX_MINER_NUM 0x100
 #define AVALON_DEFAULT_ASIC_NUM 0xA
 
 #define AVALON_AUTO_CYCLE 1024
@@ -122,7 +130,7 @@ struct avalon_info {
 	int core_voltage;
 
 	int no_matching_work;
-	int matching_work[AVALON_DEFAULT_MINER_NUM];
+	int matching_work[AVALON_MAX_MINER_NUM];
 
 	int frequency;
 	uint32_t ctlr_ver;
@@ -179,6 +187,7 @@ extern int opt_avalon_freq_min;
 extern int opt_avalon_freq_max;
 extern bool opt_avalon_auto;
 extern int opt_bitburner_core_voltage;
+extern int opt_bitburner_fury_core_voltage;
 extern char *set_avalon_fan(char *arg);
 extern char *set_avalon_freq(char *arg);
 

+ 22 - 4
driver-bflsc.c

@@ -645,18 +645,23 @@ static bool getinfo(struct cgpu_info *bflsc, int dev)
 		else if (strstr(firstname, BFLSC_DI_XLINKPRESENT))
 			sc_dev.xlink_present = strdup(fields[0]);
 		else if (strstr(firstname, BFLSC_DI_DEVICESINCHAIN)) {
-			sc_info->sc_count = atoi(fields[0]);
+			if (fields[0][0] == '0' ||
+			    (fields[0][0] == ' ' && fields[0][1] == '0'))
+				sc_info->sc_count = 1;
+			else
+				sc_info->sc_count = atoi(fields[0]);
 			if (sc_info->sc_count < 1 || sc_info->sc_count > 30) {
 				tmp = str_text(items[i]);
 				applogsiz(LOG_WARNING, BFLSC_APPLOGSIZ,
-						"%s detect (%s) invalid s-link count: '%s'",
+						"%s detect (%s) invalid x-link count: '%s'",
 						bflsc->drv->dname, bflsc->device_path, tmp);
 				free(tmp);
 				goto mata;
 			}
+		}
 		else if (strstr(firstname, BFLSC_DI_CHIPS))
 			sc_dev.chips = strdup(fields[0]);
-		}
+
 		freebreakdown(&count, &firstname, &fields);
 	}
 
@@ -813,6 +818,19 @@ reinit:
 			break;
 	}
 
+	// Set parallelization based on the getinfo() response if it is present
+	if (sc_info->sc_devs[0].chips && strlen(sc_info->sc_devs[0].chips)) {
+		if (strstr(sc_info->sc_devs[0].chips, BFLSC_DI_CHIPS_PARALLEL)) {
+			sc_info->que_noncecount = QUE_NONCECOUNT_V2;
+			sc_info->que_fld_min = QUE_FLD_MIN_V2;
+			sc_info->que_fld_max = QUE_FLD_MAX_V2;
+		} else {
+			sc_info->que_noncecount = QUE_NONCECOUNT_V1;
+			sc_info->que_fld_min = QUE_FLD_MIN_V1;
+			sc_info->que_fld_max = QUE_FLD_MAX_V1;
+		}
+	}
+
 	sc_info->scan_sleep_time = BAS_SCAN_TIME;
 	sc_info->results_sleep_time = BFLSC_RES_TIME;
 	sc_info->default_ms_work = BAS_WORK_TIME;
@@ -930,7 +948,7 @@ static void flush_one_dev(struct cgpu_info *bflsc, int dev)
 	rd_lock(&bflsc->qlock);
 
 	HASH_ITER(hh, bflsc->queued_work, work, tmp) {
-		if (work->queued && work->subid == dev) {
+		if (work->subid == dev) {
 			// devflag is used to flag stale work
 			work->devflag = true;
 			did = true;

+ 1 - 0
driver-bflsc.h

@@ -59,6 +59,7 @@ enum driver_version {
 #define BFLSC_DI_DEVICESINCHAIN "DEVICES IN CHAIN"
 #define BFLSC_DI_CHAINPRESENCE "CHAIN PRESENCE MASK"
 #define BFLSC_DI_CHIPS "CHIP PARALLELIZATION"
+#define BFLSC_DI_CHIPS_PARALLEL "YES"
 
 #define FULLNONCE 0x100000000ULL
 

+ 12 - 8
driver-icarus.c

@@ -1116,8 +1116,7 @@ static void cmr2_commands(struct cgpu_info *icarus)
 	}
 }
 
-static int64_t icarus_scanhash(struct thr_info *thr, struct work *work,
-				__maybe_unused int64_t max_nonce)
+static int64_t icarus_scanwork(struct thr_info *thr)
 {
 	struct cgpu_info *icarus = thr->cgpu;
 	struct ICARUS_INFO *info = (struct ICARUS_INFO *)(icarus->device_data);
@@ -1126,12 +1125,13 @@ static int64_t icarus_scanhash(struct thr_info *thr, struct work *work,
 	struct ICARUS_WORK workdata;
 	char *ob_hex;
 	uint32_t nonce;
-	int64_t hash_count;
+	int64_t hash_count = 0;
 	struct timeval tv_start, tv_finish, elapsed;
 	struct timeval tv_history_start, tv_history_finish;
 	double Ti, Xi;
 	int curr_hw_errors, i;
 	bool was_hw_error;
+	struct work *work;
 
 	struct ICARUS_HISTORY *history0, *history;
 	int count;
@@ -1148,6 +1148,7 @@ static int64_t icarus_scanhash(struct thr_info *thr, struct work *work,
 
 	elapsed.tv_sec = elapsed.tv_usec = 0;
 
+	work = get_work(thr, thr->id);
 	memset((void *)(&workdata), 0, sizeof(workdata));
 	memcpy(&(workdata.midstate), work->midstate, ICARUS_MIDSTATE_SIZE);
 	memcpy(&(workdata.work), work->data + ICARUS_WORK_DATA_OFFSET, ICARUS_WORK_SIZE);
@@ -1166,7 +1167,7 @@ static int64_t icarus_scanhash(struct thr_info *thr, struct work *work,
 				icarus->drv->name, icarus->device_id, err, amount);
 		dev_error(icarus, REASON_DEV_COMMS_ERROR);
 		icarus_initialise(icarus, info->baud);
-		return 0;
+		goto out;
 	}
 
 	if (opt_debug) {
@@ -1180,7 +1181,7 @@ static int64_t icarus_scanhash(struct thr_info *thr, struct work *work,
 	memset(nonce_bin, 0, sizeof(nonce_bin));
 	ret = icarus_get_nonce(icarus, nonce_bin, &tv_start, &tv_finish, thr, info->read_time);
 	if (ret == ICA_NONCE_ERROR)
-		return 0;
+		goto out;
 
 	work->blk.nonce = 0xffffffff;
 
@@ -1203,7 +1204,8 @@ static int64_t icarus_scanhash(struct thr_info *thr, struct work *work,
 				(long unsigned int)estimate_hashes,
 				elapsed.tv_sec, elapsed.tv_usec);
 
-		return estimate_hashes;
+		hash_count = estimate_hashes;
+		goto out;
 	}
 
 	memcpy((char *)&nonce, nonce_bin, sizeof(nonce_bin));
@@ -1343,7 +1345,8 @@ static int64_t icarus_scanhash(struct thr_info *thr, struct work *work,
 		timersub(&tv_history_finish, &tv_history_start, &tv_history_finish);
 		timeradd(&tv_history_finish, &(info->history_time), &(info->history_time));
 	}
-
+out:
+	free_work(work);
 	return hash_count;
 }
 
@@ -1447,11 +1450,12 @@ struct device_drv icarus_drv = {
 	.dname = "Icarus",
 	.name = "ICA",
 	.drv_detect = icarus_detect,
+	.hash_work = &hash_driver_work,
 	.get_api_stats = icarus_api_stats,
 	.get_statline_before = icarus_statline_before,
 	.set_device = icarus_set,
 	.identify_device = icarus_identify,
 	.thread_prepare = icarus_prepare,
-	.scanhash = icarus_scanhash,
+	.scanwork = icarus_scanwork,
 	.thread_shutdown = icarus_shutdown,
 };

+ 211 - 71
driver-klondike.c

@@ -44,6 +44,20 @@
 #define MAX_WORK_COUNT		4	// for now, must be binary multiple and match firmware
 #define TACH_FACTOR		87890	// fan rpm divisor
 
+/*
+ *  Work older than 5s will already be completed
+ *  FYI it must not be possible to complete 256 work
+ *  items this quickly on a single device -
+ *  thus limited to 219.9GH/s per device
+ */
+#define OLD_WORK_MS ((int)(5 * 1000))
+
+/*
+ * If the queue status hasn't been updated for this long
+ * then do it now
+ */
+#define LATE_UPDATE_MS ((int)(4 * 1000))
+
 struct device_drv klondike_drv;
 
 typedef struct klondike_header {
@@ -152,6 +166,11 @@ typedef struct klist {
 	bool working;
 } KLIST;
 
+typedef struct jobque {
+	int workqc;
+	struct timeval last_update;
+} JOBQUE;
+
 struct klondike_info {
 	bool shutdown;
 	pthread_rwlock_t stat_lock;
@@ -165,6 +184,7 @@ struct klondike_info {
 	KLIST *status;
 	DEVINFO *devinfo;
 	KLIST *cfg;
+	JOBQUE *jobque;
 	int noncecount;
 	uint64_t hashcount;
 	uint64_t errorcount;
@@ -183,6 +203,11 @@ struct klondike_info {
 	double nonce_total;
 	double nonce_min;
 	double nonce_max;
+
+	int wque_size;
+	int wque_cleared;
+
+	bool initialised;
 };
 
 static KLIST *new_klist_set(struct cgpu_info *klncgpu)
@@ -254,7 +279,7 @@ static KLIST *allocate_kitem(struct cgpu_info *klncgpu)
 	return kitem;
 }
 
-static void release_kitem(struct cgpu_info *klncgpu, KLIST *kitem)
+static KLIST *release_kitem(struct cgpu_info *klncgpu, KLIST *kitem)
 {
 	struct klondike_info *klninfo = (struct klondike_info *)(klncgpu->device_data);
 
@@ -279,6 +304,8 @@ static void release_kitem(struct cgpu_info *klncgpu, KLIST *kitem)
 	klninfo->used_count--;
 
 	cg_wunlock(&klninfo->klist_lock);
+
+	return NULL;
 }
 
 static double cvtKlnToC(uint8_t temp)
@@ -455,7 +482,9 @@ static bool klondike_get_stats(struct cgpu_info *klncgpu)
 
 	applog(LOG_DEBUG, "Klondike getting status");
 
+	rd_lock(&(klninfo->stat_lock));
 	slaves = klninfo->status[0].kline.ws.slavecount;
+	rd_unlock(&(klninfo->stat_lock));
 
 	// loop thru devices and get status for each
 	for (dev = 0; dev <= slaves; dev++) {
@@ -464,10 +493,11 @@ static bool klondike_get_stats(struct cgpu_info *klncgpu)
 		kitem = SendCmdGetReply(klncgpu, &kline, 0);
 		if (kitem != NULL) {
 			wr_lock(&(klninfo->stat_lock));
-			memcpy((void *)(&(klninfo->status[dev])), (void *)kitem, sizeof(*kitem));
+			memcpy((void *)(&(klninfo->status[dev])),
+				(void *)kitem,
+				sizeof(klninfo->status[dev]));
 			wr_unlock(&(klninfo->stat_lock));
-			release_kitem(klncgpu, kitem);
-			kitem = NULL;
+			kitem = release_kitem(klncgpu, kitem);
 		}
 	}
 
@@ -483,6 +513,8 @@ static bool klondike_init(struct cgpu_info *klncgpu)
 	KLINE kline;
 	int slaves, dev;
 
+	klninfo->initialised = false;
+
 	kline.hd.cmd = 'S';
 	kline.hd.dev = 0;
 	kitem = SendCmdGetReply(klncgpu, &kline, 0);
@@ -490,23 +522,27 @@ static bool klondike_init(struct cgpu_info *klncgpu)
 		return false;
 
 	slaves = kitem->kline.ws.slavecount;
-	release_kitem(klncgpu, kitem);
-	kitem = NULL;
 	if (klninfo->status == NULL) {
 		applog(LOG_DEBUG, "Klondike initializing data");
 
-		// alloc space for status, devinfo and cfg for master and slaves
-		klninfo->status = calloc(slaves+1, sizeof(KLIST));
+		// alloc space for status, devinfo, cfg and jobque for master and slaves
+		klninfo->status = calloc(slaves+1, sizeof(*(klninfo->status)));
 		if (unlikely(!klninfo->status))
 			quit(1, "Failed to calloc status array in klondke_get_stats");
-		klninfo->devinfo = calloc(slaves+1, sizeof(DEVINFO));
+		klninfo->devinfo = calloc(slaves+1, sizeof(*(klninfo->devinfo)));
 		if (unlikely(!klninfo->devinfo))
 			quit(1, "Failed to calloc devinfo array in klondke_get_stats");
-		klninfo->cfg = calloc(slaves+1, sizeof(KLIST));
+		klninfo->cfg = calloc(slaves+1, sizeof(*(klninfo->cfg)));
 		if (unlikely(!klninfo->cfg))
 			quit(1, "Failed to calloc cfg array in klondke_get_stats");
+		klninfo->jobque = calloc(slaves+1, sizeof(*(klninfo->jobque)));
+		if (unlikely(!klninfo->jobque))
+			quit(1, "Failed to calloc jobque array in klondke_get_stats");
 	}
 
+	memcpy((void *)(&(klninfo->status[0])), (void *)kitem, sizeof(klninfo->status[0]));
+	kitem = release_kitem(klncgpu, kitem);
+
 	// zero init triggers read back only
 	memset(&(kline.cfg), 0, sizeof(kline.cfg));
 	kline.cfg.cmd = 'C';
@@ -533,17 +569,17 @@ static bool klondike_init(struct cgpu_info *klncgpu)
 		kline.cfg.dev = dev;
 		kitem = SendCmdGetReply(klncgpu, &kline, size);
 		if (kitem != NULL) {
-			memcpy((void *)&(klninfo->cfg[dev]), kitem, sizeof(*kitem));
+			memcpy((void *)&(klninfo->cfg[dev]), kitem, sizeof(klninfo->cfg[dev]));
 			applog(LOG_WARNING, "Klondike config (%d: Clk: %d, T:%.0lf, C:%.0lf, F:%d)",
 				dev, K_HASHCLOCK(klninfo->cfg[dev].kline.cfg.hashclock),
 				cvtKlnToC(klninfo->cfg[dev].kline.cfg.temptarget),
 				cvtKlnToC(klninfo->cfg[dev].kline.cfg.tempcritical),
 				(int)100*klninfo->cfg[dev].kline.cfg.fantarget/256);
-			release_kitem(klncgpu, kitem);
-			kitem = NULL;
+			kitem = release_kitem(klncgpu, kitem);
 		}
 	}
 	klondike_get_stats(klncgpu);
+	klninfo->initialised = true;
 	for (dev = 0; dev <= slaves; dev++) {
 		klninfo->devinfo[dev].rangesize = ((uint64_t)1<<32) / klninfo->status[dev].kline.ws.chipcount;
 		klninfo->devinfo[dev].chipstats = calloc(klninfo->status[dev].kline.ws.chipcount*2 , sizeof(uint32_t));
@@ -559,12 +595,13 @@ static bool klondike_init(struct cgpu_info *klncgpu)
 	while (tries-- > 0) {
 		kitem = SendCmdGetReply(klncgpu, &kline, 1);
 		if (kitem) {
-			release_kitem(klncgpu, kitem);
-			kitem = NULL;
+			kitem = release_kitem(klncgpu, kitem);
 			ok = true;
 			break;
 		}
+		cgsleep_ms(50);
 	}
+	cgsleep_ms(50);
 
 	if (!ok)
 		applog(LOG_ERR, "%s%i: failed to enable", klncgpu->drv->name, klncgpu->device_id);
@@ -572,6 +609,21 @@ static bool klondike_init(struct cgpu_info *klncgpu)
 	return ok;
 }
 
+static void control_init(struct cgpu_info *klncgpu)
+{
+	int err, interface;
+
+	if (klncgpu->usbinfo.nodev)
+		return;
+
+	interface = usb_interface(klncgpu);
+
+	err = usb_transfer(klncgpu, 0, 9, 1, interface, C_RESET);
+
+	applog(LOG_DEBUG, "%s%i: reset got err %d",
+			  klncgpu->drv->name, klncgpu->device_id, err);
+}
+
 static bool klondike_detect_one(struct libusb_device *dev, struct usb_find_devices *found)
 {
 	struct cgpu_info *klncgpu = usb_alloc_cgpu(&klondike_drv, 1);
@@ -592,6 +644,8 @@ static bool klondike_detect_one(struct libusb_device *dev, struct usb_find_devic
 		KLIST kitem;
 		int attempts = 0;
 
+		control_init(klncgpu);
+
 		while (attempts++ < 3) {
 			err = usb_write(klncgpu, "I", 2, &sent, C_REQUESTRESULTS);
 			if (err < 0 || sent != 2) {
@@ -613,10 +667,11 @@ static bool klondike_detect_one(struct libusb_device *dev, struct usb_find_devic
 						klncgpu->device_path,
 						recd);
 			} else if (kitem.kline.hd.cmd == 'I' && kitem.kline.hd.dev == 0) {
-display_kline(klncgpu, &kitem.kline);
-				applog(LOG_DEBUG, "%s (%s) detect successful",
+				display_kline(klncgpu, &kitem.kline);
+				applog(LOG_DEBUG, "%s (%s) detect successful (%d attempt%s)",
 						  klncgpu->drv->dname,
-						  klncgpu->device_path);
+						  klncgpu->device_path,
+						  attempts, attempts == 1 ? "" : "s");
 				if (!add_cgpu(klncgpu))
 					break;
 				update_usb_stats(klncgpu);
@@ -652,7 +707,7 @@ static void klondike_identify(__maybe_unused struct cgpu_info *klncgpu)
 static void klondike_check_nonce(struct cgpu_info *klncgpu, KLIST *kitem)
 {
 	struct klondike_info *klninfo = (struct klondike_info *)(klncgpu->device_data);
-	struct work *work, *tmp;
+	struct work *work, *look, *tmp;
 	KLINE *kline = &(kitem->kline);
 	struct timeval tv_now;
 	double us_diff;
@@ -661,43 +716,54 @@ static void klondike_check_nonce(struct cgpu_info *klncgpu, KLIST *kitem)
 	applog(LOG_DEBUG, "Klondike FOUND NONCE (%02x:%08x)",
 			  kline->wr.workid, (unsigned int)nonce);
 
-	HASH_ITER(hh, klncgpu->queued_work, work, tmp) {
-		if (work->queued && (work->subid == (kline->wr.dev*256 + kline->wr.workid))) {
+	work = NULL;
+	cgtime(&tv_now);
+	rd_lock(&(klncgpu->qlock));
+	HASH_ITER(hh, klncgpu->queued_work, look, tmp) {
+		if (ms_tdiff(&tv_now, &(look->tv_stamp)) < OLD_WORK_MS &&
+		    (look->subid == (kline->wr.dev*256 + kline->wr.workid))) {
+			work = look;
+			break;
+		}
+	}
+	rd_unlock(&(klncgpu->qlock));
 
-			wr_lock(&(klninfo->stat_lock));
-			klninfo->devinfo[kline->wr.dev].noncecount++;
-			klninfo->noncecount++;
-			wr_unlock(&(klninfo->stat_lock));
+	if (work) {
+		wr_lock(&(klninfo->stat_lock));
+		klninfo->devinfo[kline->wr.dev].noncecount++;
+		klninfo->noncecount++;
+		wr_unlock(&(klninfo->stat_lock));
 
-//			kline->wr.nonce = le32toh(kline->wr.nonce - 0xC0);
-			applog(LOG_DEBUG, "Klondike SUBMIT NONCE (%02x:%08x)",
-					  kline->wr.workid, (unsigned int)nonce);
+//		kline->wr.nonce = le32toh(kline->wr.nonce - 0xC0);
+		applog(LOG_DEBUG, "Klondike SUBMIT NONCE (%02x:%08x)",
+				  kline->wr.workid, (unsigned int)nonce);
 
-			cgtime(&tv_now);
-			bool ok = submit_nonce(klncgpu->thr[0], work, nonce);
+		cgtime(&tv_now);
+		bool ok = submit_nonce(klncgpu->thr[0], work, nonce);
 
-			applog(LOG_DEBUG, "Klondike chip stats %d, %08x, %d, %d",
-					  kline->wr.dev, (unsigned int)nonce,
-					  klninfo->devinfo[kline->wr.dev].rangesize,
-					  klninfo->status[kline->wr.dev].kline.ws.chipcount);
+		applog(LOG_DEBUG, "Klondike chip stats %d, %08x, %d, %d",
+				  kline->wr.dev, (unsigned int)nonce,
+				  klninfo->devinfo[kline->wr.dev].rangesize,
+				  klninfo->status[kline->wr.dev].kline.ws.chipcount);
 
-			klninfo->devinfo[kline->wr.dev].chipstats[(nonce / klninfo->devinfo[kline->wr.dev].rangesize) + (ok ? 0 : klninfo->status[kline->wr.dev].kline.ws.chipcount)]++;
+		klninfo->devinfo[kline->wr.dev].chipstats[(nonce / klninfo->devinfo[kline->wr.dev].rangesize) + (ok ? 0 : klninfo->status[kline->wr.dev].kline.ws.chipcount)]++;
 
-			us_diff = us_tdiff(&tv_now, &(kitem->tv_when));
-			if (klninfo->delay_count == 0) {
+		us_diff = us_tdiff(&tv_now, &(kitem->tv_when));
+		if (klninfo->delay_count == 0) {
+			klninfo->delay_min = us_diff;
+			klninfo->delay_max = us_diff;
+		} else {
+			if (klninfo->delay_min > us_diff)
 				klninfo->delay_min = us_diff;
+			if (klninfo->delay_max < us_diff)
 				klninfo->delay_max = us_diff;
-			} else {
-				if (klninfo->delay_min > us_diff)
-					klninfo->delay_min = us_diff;
-				if (klninfo->delay_max < us_diff)
-					klninfo->delay_max = us_diff;
-			}
-			klninfo->delay_count++;
-			klninfo->delay_total += us_diff;
+		}
+		klninfo->delay_count++;
+		klninfo->delay_total += us_diff;
 
+		if (klninfo->nonce_count > 0) {
 			us_diff = us_tdiff(&(kitem->tv_when), &(klninfo->tv_last_nonce_received));
-			if (klninfo->nonce_count == 0) {
+			if (klninfo->nonce_count == 1) {
 				klninfo->nonce_min = us_diff;
 				klninfo->nonce_max = us_diff;
 			} else {
@@ -706,14 +772,14 @@ static void klondike_check_nonce(struct cgpu_info *klncgpu, KLIST *kitem)
 				if (klninfo->nonce_max < us_diff)
 					klninfo->nonce_max = us_diff;
 			}
-			klninfo->nonce_count++;
 			klninfo->nonce_total += us_diff;
+		}
+		klninfo->nonce_count++;
 
-			memcpy(&(klninfo->tv_last_nonce_received), &(kitem->tv_when),
-				sizeof(klninfo->tv_last_nonce_received));
+		memcpy(&(klninfo->tv_last_nonce_received), &(kitem->tv_when),
+			sizeof(klninfo->tv_last_nonce_received));
 
-			return;
-		}
+		return;
 	}
 
 	applog(LOG_ERR, "%s%i:%d unknown work (%02x:%08x) - ignored",
@@ -730,9 +796,9 @@ static void *klondike_get_replies(void *userdata)
 	struct klondike_info *klninfo = (struct klondike_info *)(klncgpu->device_data);
 	KLIST *kitem = NULL;
 	char *hexdata;
-	int err, recd;
+	int err, recd, slaves;
 
-	applog(LOG_ERR, "Klondike listening for replies");
+	applog(LOG_DEBUG, "Klondike listening for replies");
 
 	while (klninfo->shutdown == false) {
 		if (klncgpu->usbinfo.nodev)
@@ -755,6 +821,22 @@ static void *klondike_get_replies(void *userdata)
 				free(hexdata);
 			}
 
+			// We can't check this until it's initialised
+			if (klninfo->initialised) {
+				rd_lock(&(klninfo->stat_lock));
+				slaves = klninfo->status[0].kline.ws.slavecount;
+				rd_unlock(&(klninfo->stat_lock));
+
+				if (kitem->kline.hd.dev > slaves) {
+					applog(LOG_ERR, "%s%i: reply [%c] has invalid dev=%d (max=%d) using 0",
+							klncgpu->drv->name, klncgpu->device_id,
+							(char)(kitem->kline.hd.cmd),
+							(int)(kitem->kline.hd.dev),
+							slaves);
+					kitem->kline.hd.dev = 0;
+				}
+			}
+
 			switch (kitem->kline.hd.cmd) {
 				case '=':
 					klondike_check_nonce(klncgpu, kitem);
@@ -763,6 +845,25 @@ static void *klondike_get_replies(void *userdata)
 				case 'S':
 				case 'W':
 				case 'A':
+					// We can't do/check this until it's initialised
+					if (klninfo->initialised) {
+						wr_lock(&(klninfo->stat_lock));
+						klninfo->jobque[kitem->kline.ws.dev].workqc =
+									(int)(kitem->kline.ws.workqc);
+						cgtime(&(klninfo->jobque[kitem->kline.ws.dev].last_update));
+						slaves = klninfo->status[0].kline.ws.slavecount;
+						wr_unlock(&(klninfo->stat_lock));
+
+						if (kitem->kline.ws.slavecount != slaves) {
+							applog(LOG_ERR, "%s%i: reply [%c] has a diff # of slaves=%d (curr=%d) dropping device to hotplug",
+									klncgpu->drv->name, klncgpu->device_id,
+									(char)(kitem->kline.ws.cmd),
+									(int)(kitem->kline.ws.slavecount),
+									slaves);
+							klninfo->shutdown = true;
+							break;
+						}
+					}
 				case 'E':
 					wr_lock(&(klninfo->stat_lock));
 					klninfo->errorcount += kitem->kline.ws.errorcount;
@@ -801,17 +902,20 @@ static void klondike_flush_work(struct cgpu_info *klncgpu)
 	klninfo->block_seq++;
 
 	applog(LOG_DEBUG, "Klondike flushing work");
+	rd_lock(&(klninfo->stat_lock));
 	slaves = klninfo->status[0].kline.ws.slavecount;
+	rd_unlock(&(klninfo->stat_lock));
 	kline.hd.cmd = 'A';
 	for (dev = 0; dev <= slaves; dev++) {
 		kline.hd.dev = dev;
 		kitem = SendCmdGetReply(klncgpu, &kline, KSENDHD(0));
 		if (kitem != NULL) {
 			wr_lock(&(klninfo->stat_lock));
-			memcpy((void *)&(klninfo->status[dev]), kitem, sizeof(*kitem));
+			memcpy((void *)&(klninfo->status[dev]),
+				kitem,
+				sizeof(klninfo->status[dev]));
 			wr_unlock(&(klninfo->stat_lock));
-			release_kitem(klncgpu, kitem);
-			kitem = NULL;
+			kitem = release_kitem(klncgpu, kitem);
 		}
 	}
 }
@@ -860,7 +964,7 @@ static void klondike_shutdown(struct thr_info *thr)
 		kline.hd.buf[0] = '0';
 		kitem = SendCmdGetReply(klncgpu, &kline, KSENDHD(1));
 		if (kitem)
-			release_kitem(klncgpu, kitem);
+			kitem = release_kitem(klncgpu, kitem);
 	}
 	klncgpu->shutdown = klninfo->shutdown = true;
 }
@@ -886,8 +990,10 @@ static void klondike_thread_enable(struct thr_info *thr)
 static bool klondike_send_work(struct cgpu_info *klncgpu, int dev, struct work *work)
 {
 	struct klondike_info *klninfo = (struct klondike_info *)(klncgpu->device_data);
-	struct work *tmp;
+	struct work *look, *tmp;
 	KLINE kline;
+	struct timeval tv_old;
+	int wque_size, wque_cleared;
 
 	if (klncgpu->usbinfo.nodev)
 		return false;
@@ -898,6 +1004,7 @@ static bool klondike_send_work(struct cgpu_info *klncgpu, int dev, struct work *
 	memcpy(kline.wt.merkle, work->data + MERKLE_OFFSET, MERKLE_BYTES);
 	kline.wt.workid = (uint8_t)(klninfo->devinfo[dev].nextworkid++ & 0xFF);
 	work->subid = dev*256 + kline.wt.workid;
+	cgtime(&work->tv_stamp);
 
 	if (opt_log_level <= LOG_DEBUG) {
 		char *hexdata = bin2hex((void *)&kline.wt, sizeof(kline.wt));
@@ -909,16 +1016,28 @@ static bool klondike_send_work(struct cgpu_info *klncgpu, int dev, struct work *
 	KLIST *kitem = SendCmdGetReply(klncgpu, &kline, sizeof(kline.wt));
 	if (kitem != NULL) {
 		wr_lock(&(klninfo->stat_lock));
-		memcpy((void *)&(klninfo->status[dev]), kitem, sizeof(*kitem));
+		memcpy((void *)&(klninfo->status[dev]), kitem, sizeof(klninfo->status[dev]));
 		wr_unlock(&(klninfo->stat_lock));
-		release_kitem(klncgpu, kitem);
-		kitem = NULL;
+		kitem = release_kitem(klncgpu, kitem);
 
 		// remove old work
-		HASH_ITER(hh, klncgpu->queued_work, work, tmp) {
-		if (work->queued && (work->subid == (int)(dev*256 + ((klninfo->devinfo[dev].nextworkid-2*MAX_WORK_COUNT) & 0xFF))))
-			work_completed(klncgpu, work);
+		wque_size = 0;
+		wque_cleared = 0;
+		cgtime(&tv_old);
+		wr_lock(&klncgpu->qlock);
+		HASH_ITER(hh, klncgpu->queued_work, look, tmp) {
+			if (ms_tdiff(&tv_old, &(look->tv_stamp)) > OLD_WORK_MS) {
+				__work_completed(klncgpu, look);
+				free_work(look);
+			} else
+				wque_size++;
 		}
+		wr_unlock(&klncgpu->qlock);
+
+		wr_lock(&(klninfo->stat_lock));
+		klninfo->wque_size = wque_size;
+		klninfo->wque_cleared = wque_cleared;
+		wr_unlock(&(klninfo->stat_lock));
 		return true;
 	}
 	return false;
@@ -929,20 +1048,38 @@ static bool klondike_queue_full(struct cgpu_info *klncgpu)
 	struct klondike_info *klninfo = (struct klondike_info *)(klncgpu->device_data);
 	struct work *work = NULL;
 	int dev, queued, slaves;
+	struct timeval now;
+
 
+	cgtime(&now);
+	rd_lock(&(klninfo->stat_lock));
 	slaves = klninfo->status[0].kline.ws.slavecount;
+	for (dev = 0; dev <= slaves; dev++)
+		if (ms_tdiff(&now, &(klninfo->jobque[dev].last_update)) > LATE_UPDATE_MS) {
+			rd_unlock(&(klninfo->stat_lock));
+			applog(LOG_ERR, "%s%i: late update",
+						klncgpu->drv->name, klncgpu->device_id);
+			klondike_get_stats(klncgpu);
+			goto que;
+		}
+	rd_unlock(&(klninfo->stat_lock));
+
+que:
+
 	for (queued = 0; queued < MAX_WORK_COUNT-1; queued++)
-		for (dev = 0; dev <= slaves; dev++)
-			if (klninfo->status[dev].kline.ws.workqc <= queued) {
+		for (dev = 0; dev <= slaves; dev++) {
+			rd_lock(&(klninfo->stat_lock));
+			if (klninfo->jobque[dev].workqc <= queued) {
+				rd_unlock(&(klninfo->stat_lock));
 				if (!work)
 					work = get_queued(klncgpu);
 				if (unlikely(!work))
 					return false;
-				if (klondike_send_work(klncgpu, dev, work)) {
-					work = NULL;
-					break;
-				}
-			}
+				if (klondike_send_work(klncgpu, dev, work))
+					return false;
+			} else
+				rd_unlock(&(klninfo->stat_lock));
+		}
 
 	return true;
 }
@@ -1100,6 +1237,9 @@ static struct api_data *klondike_api_stats(struct cgpu_info *klncgpu)
 		avg = klninfo->nonce_total / klninfo->nonce_count;
 	root = api_add_diff(root, "KQue Nonce Avg", &avg, true);
 
+	root = api_add_int(root, "WQue Size", &(klninfo->wque_size), true);
+	root = api_add_int(root, "WQue Cleared", &(klninfo->wque_cleared), true);
+
 	rd_unlock(&(klninfo->stat_lock));
 
 	return root;

+ 130 - 51
miner.h

@@ -575,6 +575,7 @@ struct cgpu_info {
 
 	pthread_rwlock_t qlock;
 	struct work *queued_work;
+	struct work *unqueued_work;
 	unsigned int queued_count;
 
 	bool shutdown;
@@ -738,74 +739,143 @@ endian_flip128(void __maybe_unused *dest_p, const void __maybe_unused *src_p)
 
 extern void _quit(int status);
 
-#define mutex_lock(_lock) _mutex_lock(_lock, __FILE__, __func__, __LINE__) 
-#define mutex_unlock_noyield(_lock) _mutex_unlock_noyield(_lock, __FILE__, __func__, __LINE__) 
-#define wr_lock(_lock) _wr_lock(_lock, __FILE__, __func__, __LINE__) 
-#define rd_lock(_lock) _rd_lock(_lock, __FILE__, __func__, __LINE__) 
-#define rw_unlock(_lock) _rw_unlock(_lock, __FILE__, __func__, __LINE__) 
-#define mutex_init(_lock) _mutex_init(_lock, __FILE__, __func__, __LINE__) 
-#define rwlock_init(_lock) _rwlock_init(_lock, __FILE__, __func__, __LINE__) 
+/*
+ * Set this to non-zero to enable lock tracking
+ * Use the API lockstats command to see the locking status on stderr
+ *  i.e. in your log file if you 2> log.log - but not on the screen
+ * API lockstats is privilidged but will always exist and will return
+ *	success if LOCK_TRACKING is enabled and warning if disabled
+ * In production code, this should never be enabled since it will slow down all locking
+ * So, e.g. use it to track down a deadlock - after a reproducable deadlock occurs
+ * ... Of course if the API code itself deadlocks, it wont help :)
+ */
+#define LOCK_TRACKING 0
+
+#if LOCK_TRACKING
+enum cglock_typ {
+	CGLOCK_MUTEX,
+	CGLOCK_RW,
+	CGLOCK_UNKNOWN
+};
+
+extern uint64_t api_getlock(void *lock, const char *file, const char *func, const int line);
+extern void api_gotlock(uint64_t id, void *lock, const char *file, const char *func, const int line);
+extern uint64_t api_trylock(void *lock, const char *file, const char *func, const int line);
+extern void api_didlock(uint64_t id, int ret, void *lock, const char *file, const char *func, const int line);
+extern void api_gunlock(void *lock, const char *file, const char *func, const int line);
+extern void api_initlock(void *lock, enum cglock_typ typ, const char *file, const char *func, const int line);
+
+#define GETLOCK(_lock, _file, _func, _line) uint64_t _id1 = api_getlock((void *)(_lock), _file, _func, _line)
+#define GOTLOCK(_lock, _file, _func, _line) api_gotlock(_id1, (void *)(_lock), _file, _func, _line)
+#define TRYLOCK(_lock, _file, _func, _line) uint64_t _id2 = api_trylock((void *)(_lock), _file, _func, _line)
+#define DIDLOCK(_ret, _lock, _file, _func, _line) api_didlock(_id2, _ret, (void *)(_lock), _file, _func, _line)
+#define GUNLOCK(_lock, _file, _func, _line) api_gunlock((void *)(_lock), _file, _func, _line)
+#define INITLOCK(_lock, _typ, _file, _func, _line) api_initlock((void *)(_lock), _typ, _file, _func, _line)
+#else
+#define GETLOCK(_lock, _file, _func, _line)
+#define GOTLOCK(_lock, _file, _func, _line)
+#define TRYLOCK(_lock, _file, _func, _line)
+#define DIDLOCK(_ret, _lock, _file, _func, _line)
+#define GUNLOCK(_lock, _file, _func, _line)
+#define INITLOCK(_typ, _lock, _file, _func, _line)
+#endif
+
+#define mutex_lock(_lock) _mutex_lock(_lock, __FILE__, __func__, __LINE__)
+#define mutex_unlock_noyield(_lock) _mutex_unlock_noyield(_lock, __FILE__, __func__, __LINE__)
+#define mutex_unlock(_lock) _mutex_unlock(_lock, __FILE__, __func__, __LINE__)
+#define mutex_trylock(_lock) _mutex_trylock(_lock, __FILE__, __func__, __LINE__)
+#define wr_lock(_lock) _wr_lock(_lock, __FILE__, __func__, __LINE__)
+#define rd_lock(_lock) _rd_lock(_lock, __FILE__, __func__, __LINE__)
+#define rw_unlock(_lock) _rw_unlock(_lock, __FILE__, __func__, __LINE__)
+#define rd_unlock_noyield(_lock) _rd_unlock_noyield(_lock, __FILE__, __func__, __LINE__)
+#define wr_unlock_noyield(_lock) _wr_unlock_noyield(_lock, __FILE__, __func__, __LINE__)
+#define rd_unlock(_lock) _rd_unlock(_lock, __FILE__, __func__, __LINE__)
+#define wr_unlock(_lock) _wr_unlock(_lock, __FILE__, __func__, __LINE__)
+#define mutex_init(_lock) _mutex_init(_lock, __FILE__, __func__, __LINE__)
+#define rwlock_init(_lock) _rwlock_init(_lock, __FILE__, __func__, __LINE__)
+#define cglock_init(_lock) _cglock_init(_lock, __FILE__, __func__, __LINE__)
+#define cg_rlock(_lock) _cg_rlock(_lock, __FILE__, __func__, __LINE__)
+#define cg_ilock(_lock) _cg_ilock(_lock, __FILE__, __func__, __LINE__)
+#define cg_ulock(_lock) _cg_ulock(_lock, __FILE__, __func__, __LINE__)
+#define cg_wlock(_lock) _cg_wlock(_lock, __FILE__, __func__, __LINE__)
+#define cg_dwlock(_lock) _cg_dwlock(_lock, __FILE__, __func__, __LINE__)
+#define cg_dwilock(_lock) _cg_dwilock(_lock, __FILE__, __func__, __LINE__)
+#define cg_dlock(_lock) _cg_dlock(_lock, __FILE__, __func__, __LINE__)
+#define cg_runlock(_lock) _cg_runlock(_lock, __FILE__, __func__, __LINE__)
+#define cg_ruwlock(_lock) _cg_ruwlock(_lock, __FILE__, __func__, __LINE__)
+#define cg_wunlock(_lock) _cg_wunlock(_lock, __FILE__, __func__, __LINE__)
 
 static inline void _mutex_lock(pthread_mutex_t *lock, const char *file, const char *func, const int line)
 {
+	GETLOCK(lock, file, func, line);
 	if (unlikely(pthread_mutex_lock(lock)))
 		quitfrom(1, file, func, line, "WTF MUTEX ERROR ON LOCK! errno=%d", errno);
+	GOTLOCK(lock, file, func, line);
 }
 
 static inline void _mutex_unlock_noyield(pthread_mutex_t *lock, const char *file, const char *func, const int line)
 {
 	if (unlikely(pthread_mutex_unlock(lock)))
 		quitfrom(1, file, func, line, "WTF MUTEX ERROR ON UNLOCK! errno=%d", errno);
+	GUNLOCK(lock, file, func, line);
 }
 
-static inline void mutex_unlock(pthread_mutex_t *lock)
+static inline void _mutex_unlock(pthread_mutex_t *lock, const char *file, const char *func, const int line)
 {
-	mutex_unlock_noyield(lock);
+	_mutex_unlock_noyield(lock, file, func, line);
 	sched_yield();
 }
 
-static inline int mutex_trylock(pthread_mutex_t *lock)
+static inline int _mutex_trylock(pthread_mutex_t *lock, __maybe_unused const char *file, __maybe_unused const char *func, __maybe_unused const int line)
 {
-	return pthread_mutex_trylock(lock);
+	TRYLOCK(lock, file, func, line);
+	int ret = pthread_mutex_trylock(lock);
+	DIDLOCK(ret, lock, file, func, line);
+	return ret;
 }
 
 static inline void _wr_lock(pthread_rwlock_t *lock, const char *file, const char *func, const int line)
 {
+	GETLOCK(lock, file, func, line);
 	if (unlikely(pthread_rwlock_wrlock(lock)))
 		quitfrom(1, file, func, line, "WTF WRLOCK ERROR ON LOCK! errno=%d", errno);
+	GOTLOCK(lock, file, func, line);
 }
 
 static inline void _rd_lock(pthread_rwlock_t *lock, const char *file, const char *func, const int line)
 {
+	GETLOCK(lock, file, func, line);
 	if (unlikely(pthread_rwlock_rdlock(lock)))
 		quitfrom(1, file, func, line, "WTF RDLOCK ERROR ON LOCK! errno=%d", errno);
+	GOTLOCK(lock, file, func, line);
 }
 
 static inline void _rw_unlock(pthread_rwlock_t *lock, const char *file, const char *func, const int line)
 {
 	if (unlikely(pthread_rwlock_unlock(lock)))
 		quitfrom(1, file, func, line, "WTF RWLOCK ERROR ON UNLOCK! errno=%d", errno);
+	GUNLOCK(lock, file, func, line);
 }
 
-static inline void rd_unlock_noyield(pthread_rwlock_t *lock)
+static inline void _rd_unlock_noyield(pthread_rwlock_t *lock, const char *file, const char *func, const int line)
 {
-	rw_unlock(lock);
+	_rw_unlock(lock, file, func, line);
 }
 
-static inline void wr_unlock_noyield(pthread_rwlock_t *lock)
+static inline void _wr_unlock_noyield(pthread_rwlock_t *lock, const char *file, const char *func, const int line)
 {
-	rw_unlock(lock);
+	_rw_unlock(lock, file, func, line);
 }
 
-static inline void rd_unlock(pthread_rwlock_t *lock)
+static inline void _rd_unlock(pthread_rwlock_t *lock, const char *file, const char *func, const int line)
 {
-	rw_unlock(lock);
+	_rw_unlock(lock, file, func, line);
 	sched_yield();
 }
 
-static inline void wr_unlock(pthread_rwlock_t *lock)
+static inline void _wr_unlock(pthread_rwlock_t *lock, const char *file, const char *func, const int line)
 {
-	rw_unlock(lock);
+	_rw_unlock(lock, file, func, line);
 	sched_yield();
 }
 
@@ -813,86 +883,88 @@ static inline void _mutex_init(pthread_mutex_t *lock, const char *file, const ch
 {
 	if (unlikely(pthread_mutex_init(lock, NULL)))
 		quitfrom(1, file, func, line, "Failed to pthread_mutex_init errno=%d", errno);
+	INITLOCK(lock, CGLOCK_MUTEX, file, func, line);
 }
 
 static inline void _rwlock_init(pthread_rwlock_t *lock, const char *file, const char *func, const int line)
 {
 	if (unlikely(pthread_rwlock_init(lock, NULL)))
 		quitfrom(1, file, func, line, "Failed to pthread_rwlock_init errno=%d", errno);
+	INITLOCK(lock, CGLOCK_RW, file, func, line);
 }
 
-static inline void cglock_init(cglock_t *lock)
+static inline void _cglock_init(cglock_t *lock, const char *file, const char *func, const int line)
 {
-	mutex_init(&lock->mutex);
-	rwlock_init(&lock->rwlock);
+	_mutex_init(&lock->mutex, file, func, line);
+	_rwlock_init(&lock->rwlock, file, func, line);
 }
 
 /* Read lock variant of cglock. Cannot be promoted. */
-static inline void cg_rlock(cglock_t *lock)
+static inline void _cg_rlock(cglock_t *lock, const char *file, const char *func, const int line)
 {
-	mutex_lock(&lock->mutex);
-	rd_lock(&lock->rwlock);
-	mutex_unlock_noyield(&lock->mutex);
+	_mutex_lock(&lock->mutex, file, func, line);
+	_rd_lock(&lock->rwlock, file, func, line);
+	_mutex_unlock_noyield(&lock->mutex, file, func, line);
 }
 
 /* Intermediate variant of cglock - behaves as a read lock but can be promoted
  * to a write lock or demoted to read lock. */
-static inline void cg_ilock(cglock_t *lock)
+static inline void _cg_ilock(cglock_t *lock, const char *file, const char *func, const int line)
 {
-	mutex_lock(&lock->mutex);
+	_mutex_lock(&lock->mutex, file, func, line);
 }
 
 /* Upgrade intermediate variant to a write lock */
-static inline void cg_ulock(cglock_t *lock)
+static inline void _cg_ulock(cglock_t *lock, const char *file, const char *func, const int line)
 {
-	wr_lock(&lock->rwlock);
+	_wr_lock(&lock->rwlock, file, func, line);
 }
 
 /* Write lock variant of cglock */
-static inline void cg_wlock(cglock_t *lock)
+static inline void _cg_wlock(cglock_t *lock, const char *file, const char *func, const int line)
 {
-	mutex_lock(&lock->mutex);
-	wr_lock(&lock->rwlock);
+	_mutex_lock(&lock->mutex, file, func, line);
+	_wr_lock(&lock->rwlock, file, func, line);
 }
 
 /* Downgrade write variant to a read lock */
-static inline void cg_dwlock(cglock_t *lock)
+static inline void _cg_dwlock(cglock_t *lock, const char *file, const char *func, const int line)
 {
-	wr_unlock_noyield(&lock->rwlock);
-	rd_lock(&lock->rwlock);
-	mutex_unlock_noyield(&lock->mutex);
+	_wr_unlock_noyield(&lock->rwlock, file, func, line);
+	_rd_lock(&lock->rwlock, file, func, line);
+	_mutex_unlock_noyield(&lock->mutex, file, func, line);
 }
 
 /* Demote a write variant to an intermediate variant */
-static inline void cg_dwilock(cglock_t *lock)
+static inline void _cg_dwilock(cglock_t *lock, const char *file, const char *func, const int line)
 {
-	wr_unlock(&lock->rwlock);
+	_wr_unlock(&lock->rwlock, file, func, line);
 }
 
 /* Downgrade intermediate variant to a read lock */
-static inline void cg_dlock(cglock_t *lock)
+static inline void _cg_dlock(cglock_t *lock, const char *file, const char *func, const int line)
 {
-	rd_lock(&lock->rwlock);
-	mutex_unlock_noyield(&lock->mutex);
+	_rd_lock(&lock->rwlock, file, func, line);
+	_mutex_unlock_noyield(&lock->mutex, file, func, line);
 }
 
-static inline void cg_runlock(cglock_t *lock)
+static inline void _cg_runlock(cglock_t *lock, const char *file, const char *func, const int line)
 {
-	rd_unlock(&lock->rwlock);
+	_rd_unlock(&lock->rwlock, file, func, line);
 }
 
 /* This drops the read lock and grabs a write lock. It does NOT protect data
  * between the two locks! */
-static inline void cg_ruwlock(cglock_t *lock)
+static inline void _cg_ruwlock(cglock_t *lock, const char *file, const char *func, const int line)
 {
-	rd_unlock_noyield(&lock->rwlock);
-	cg_wlock(lock);
+	_rd_unlock_noyield(&lock->rwlock, file, func, line);
+	_cg_wlock(lock, file, func, line);
 }
 
-static inline void cg_wunlock(cglock_t *lock)
+static inline void _cg_wunlock(cglock_t *lock, const char *file, const char *func, const int line)
 {
-	wr_unlock_noyield(&lock->rwlock);
-	mutex_unlock(&lock->mutex);
+	_wr_unlock_noyield(&lock->rwlock, file, func, line);
+	_mutex_unlock(&lock->mutex, file, func, line);
 }
 
 struct pool;
@@ -943,6 +1015,10 @@ extern bool opt_bfl_noncerange;
 #endif
 extern int swork_id;
 
+#if LOCK_TRACKING
+extern pthread_mutex_t lockstat_lock;
+#endif
+
 extern pthread_rwlock_t netacc_lock;
 
 extern const uint32_t sha256_init_state[];
@@ -1066,6 +1142,7 @@ extern struct pool **pools;
 extern struct strategies strategies[];
 extern enum pool_strategy pool_strategy;
 extern int opt_rotate_period;
+extern double total_rolling;
 extern double total_mhashes_done;
 extern unsigned int new_blocks;
 extern unsigned int found_blocks;
@@ -1306,7 +1383,6 @@ struct work {
 	bool		stale;
 	bool		mandatory;
 	bool		block;
-	bool		queued;
 
 	bool		stratum;
 	char 		*job_id;
@@ -1330,6 +1406,8 @@ struct work {
 	int		subid;
 	// Allow devices to flag work for their own purposes
 	bool		devflag;
+	// Allow devices to timestamp work for their own purposes
+	struct timeval	tv_stamp;
 
 	struct timeval	tv_getwork;
 	struct timeval	tv_getwork_reply;
@@ -1391,6 +1469,7 @@ extern struct work *get_queued(struct cgpu_info *cgpu);
 extern struct work *__find_work_bymidstate(struct work *que, char *midstate, size_t midstatelen, char *data, int offset, size_t datalen);
 extern struct work *find_queued_work_bymidstate(struct cgpu_info *cgpu, char *midstate, size_t midstatelen, char *data, int offset, size_t datalen);
 extern struct work *clone_queued_work_bymidstate(struct cgpu_info *cgpu, char *midstate, size_t midstatelen, char *data, int offset, size_t datalen);
+extern void __work_completed(struct cgpu_info *cgpu, struct work *work);
 extern void work_completed(struct cgpu_info *cgpu, struct work *work);
 extern struct work *take_queued_work_bymidstate(struct cgpu_info *cgpu, char *midstate, size_t midstatelen, char *data, int offset, size_t datalen);
 extern void hash_driver_work(struct thr_info *mythr);

+ 61 - 24
usbutils.c

@@ -330,6 +330,18 @@ static struct usb_find_devices find_dev[] = {
 		.timeout = AVALON_TIMEOUT_MS,
 		.latency = 10,
 		INTINFO(ava_ints) },
+	{
+		.drv = DRIVER_avalon,
+		.name = "BBF",
+		.ident = IDENT_BBF,
+		.idVendor = IDVENDOR_FTDI,
+		.idProduct = 0x6001,
+		.iManufacturer = "Burnin Electronics",
+		.iProduct = "BitBurner Fury",
+		.config = 1,
+		.timeout = AVALON_TIMEOUT_MS,
+		.latency = 10,
+		INTINFO(ava_ints) },
 	{
 		.drv = DRIVER_avalon,
 		.name = "AVA",
@@ -833,11 +845,11 @@ static void usb_full(ssize_t *count, libusb_device *dev, char **buf, size_t *off
 
 	err = libusb_get_string_descriptor_ascii(handle, desc.iManufacturer, man, STRBUFLEN);
 	if (err < 0)
-		snprintf((char *)man, sizeof(man), "** err(%d)%s", err, libusb_error_name(err));
+		snprintf((char *)man, sizeof(man), "** err:(%d) %s", err, libusb_error_name(err));
 
 	err = libusb_get_string_descriptor_ascii(handle, desc.iProduct, prod, STRBUFLEN);
 	if (err < 0)
-		snprintf((char *)prod, sizeof(prod), "** err(%d)%s", err, libusb_error_name(err));
+		snprintf((char *)prod, sizeof(prod), "** err:(%d) %s", err, libusb_error_name(err));
 
 	if (level == 0) {
 		libusb_close(handle);
@@ -913,7 +925,7 @@ static void usb_full(ssize_t *count, libusb_device *dev, char **buf, size_t *off
 
 	err = libusb_get_string_descriptor_ascii(handle, desc.iSerialNumber, ser, STRBUFLEN);
 	if (err < 0)
-		snprintf((char *)ser, sizeof(ser), "** err(%d)%s", err, libusb_error_name(err));
+		snprintf((char *)ser, sizeof(ser), "** err:(%d) %s", err, libusb_error_name(err));
 
 	snprintf(tmp, sizeof(tmp), EOL "     dev %d: More Info:" EOL "\tManufacturer: '%s'" EOL
 			"\tProduct: '%s'" EOL "\tSerial '%s'",
@@ -933,7 +945,7 @@ void usb_all(int level)
 
 	count = libusb_get_device_list(NULL, &list);
 	if (count < 0) {
-		applog(LOG_ERR, "USB all: failed, err %d%s", (int)count, libusb_error_name((int)count));
+		applog(LOG_ERR, "USB all: failed, err:(%d) %s", (int)count, libusb_error_name((int)count));
 		return;
 	}
 
@@ -2242,6 +2254,12 @@ static void init_usb_transfer(struct usb_transfer *ut)
 	ut->transfer->user_data = ut;
 }
 
+static void complete_usb_transfer(struct usb_transfer *ut)
+{
+	cgsem_destroy(&ut->cgsem);
+	libusb_free_transfer(ut->transfer);
+}
+
 static void LIBUSB_CALL transfer_callback(struct libusb_transfer *transfer)
 {
 	struct usb_transfer *ut = transfer->user_data;
@@ -2265,7 +2283,7 @@ static int callback_wait(struct usb_transfer *ut, int *transferred, unsigned int
 		cgsem_wait(&ut->cgsem);
 	}
 	ret = transfer->status;
-	if (ret == LIBUSB_TRANSFER_CANCELLED)
+	if (ret == LIBUSB_TRANSFER_CANCELLED || ret == LIBUSB_TRANSFER_TIMED_OUT)
 		ret = LIBUSB_ERROR_TIMEOUT;
 
 	/* No need to sort out mutexes here since they won't be reused */
@@ -2294,6 +2312,11 @@ usb_bulk_transfer(struct libusb_device_handle *dev_handle, int intinfo,
 	usb_epinfo = &(cgpu->usbdev->found->intinfos[intinfo].epinfos[epinfo]);
 	endpoint = usb_epinfo->ep;
 
+	/* Avoid any async transfers during shutdown to allow the polling
+	 * thread to be shut down after all existing transfers are complete */
+	if (unlikely(cgpu->shutdown))
+		return libusb_bulk_transfer(dev_handle, endpoint, data, length, transferred, timeout);
+
 	/* Limit length of transfer to the largest this descriptor supports
 	 * and leave the higher level functions to transfer more if needed. */
 	if (usb_epinfo->PrefPacketSize)
@@ -2308,10 +2331,15 @@ usb_bulk_transfer(struct libusb_device_handle *dev_handle, int intinfo,
 	USBDEBUG("USB debug: @usb_bulk_transfer(%s (nodev=%s),intinfo=%d,epinfo=%d,data=%p,length=%d,timeout=%u,mode=%d,cmd=%s,seq=%d) endpoint=%d", cgpu->drv->name, bool_str(cgpu->usbinfo.nodev), intinfo, epinfo, data, length, timeout, mode, usb_cmdname(cmd), seq, (int)endpoint);
 
 	init_usb_transfer(&ut);
+#ifdef LINUX
 	/* We give the transfer no timeout since we manage timeouts ourself */
 	libusb_fill_bulk_transfer(ut.transfer, dev_handle, endpoint, buf, length,
 				  transfer_callback, &ut, 0);
-
+#else
+	/* All other OSes not so lucky */
+	libusb_fill_bulk_transfer(ut.transfer, dev_handle, endpoint, buf, length,
+				  transfer_callback, &ut, timeout);
+#endif
 	STATS_TIMEVAL(&tv_start);
 	cg_rlock(&cgusb_fd_lock);
 	err = libusb_submit_transfer(ut.transfer);
@@ -2319,7 +2347,7 @@ usb_bulk_transfer(struct libusb_device_handle *dev_handle, int intinfo,
 	errn = errno;
 	if (!err)
 		err = callback_wait(&ut, transferred, timeout);
-	libusb_free_transfer(ut.transfer);
+	complete_usb_transfer(&ut);
 
 	STATS_TIMEVAL(&tv_finish);
 	USB_STATS(cgpu, &tv_start, &tv_finish, err, mode, cmd, seq, timeout);
@@ -2612,8 +2640,8 @@ int _usb_read(struct cgpu_info *cgpu, int intinfo, int epinfo, char *buf, size_t
 
 out_unlock:
 	if (err && err != LIBUSB_ERROR_TIMEOUT) {
-		applog(LOG_WARNING, "%s %i usb read error: %s", cgpu->drv->name, cgpu->device_id,
-		       libusb_error_name(err));
+		applog(LOG_WARNING, "%s %i usb read err:(%d) %s", cgpu->drv->name, cgpu->device_id,
+		       err, libusb_error_name(err));
 		if (cgpu->usbinfo.continuous_ioerr_count > USB_RETRY_MAX)
 			err = LIBUSB_ERROR_OTHER;
 	}
@@ -2712,8 +2740,8 @@ int _usb_write(struct cgpu_info *cgpu, int intinfo, int epinfo, char *buf, size_
 	*processed = tot;
 
 	if (err) {
-		applog(LOG_WARNING, "%s %i usb write error: %s", cgpu->drv->name, cgpu->device_id,
-		       libusb_error_name(err));
+		applog(LOG_WARNING, "%s %i usb write err:(%d) %s", cgpu->drv->name, cgpu->device_id,
+		       err, libusb_error_name(err));
 		if (cgpu->usbinfo.continuous_ioerr_count > USB_RETRY_MAX)
 			err = LIBUSB_ERROR_OTHER;
 	}
@@ -2731,7 +2759,7 @@ out_noerrmsg:
 /* As we do for bulk reads, emulate a sync function for control transfers using
  * our own timeouts that takes the same parameters as libusb_control_transfer.
  */
-static int usb_control_transfer(libusb_device_handle *dev_handle, uint8_t bmRequestType,
+static int usb_control_transfer(struct cgpu_info *cgpu, libusb_device_handle *dev_handle, uint8_t bmRequestType,
 				uint8_t bRequest, uint16_t wValue, uint16_t wIndex,
 				unsigned char *buffer, uint16_t wLength, unsigned int timeout)
 {
@@ -2739,25 +2767,35 @@ static int usb_control_transfer(libusb_device_handle *dev_handle, uint8_t bmRequ
 	unsigned char buf[70];
 	int err, transferred;
 
+	if (unlikely(cgpu->shutdown))
+		return libusb_control_transfer(dev_handle, bmRequestType, bRequest, wValue, wIndex, buffer, wLength, timeout);
+
 	init_usb_transfer(&ut);
 	libusb_fill_control_setup(buf, bmRequestType, bRequest, wValue,
 				  wIndex, wLength);
+	if ((bmRequestType & LIBUSB_ENDPOINT_DIR_MASK) == LIBUSB_ENDPOINT_OUT)
+		memcpy(buf + LIBUSB_CONTROL_SETUP_SIZE, buffer, wLength);
+#ifdef LINUX
 	libusb_fill_control_transfer(ut.transfer, dev_handle, buf, transfer_callback,
 				     &ut, 0);
+#else
+	libusb_fill_control_transfer(ut.transfer, dev_handle, buf, transfer_callback,
+				     &ut, timeout);
+#endif
 	err = libusb_submit_transfer(ut.transfer);
 	if (!err)
 		err = callback_wait(&ut, &transferred, timeout);
-	if (!err && transferred) {
-		unsigned char *ofbuf = libusb_control_transfer_get_data(ut.transfer);
-
-		memcpy(buffer, ofbuf, transferred);
+	if (err == LIBUSB_TRANSFER_COMPLETED && transferred) {
+		if ((bmRequestType & LIBUSB_ENDPOINT_DIR_MASK) == LIBUSB_ENDPOINT_IN)
+			memcpy(buffer, libusb_control_transfer_get_data(ut.transfer),
+			       transferred);
 		err = transferred;
 		goto out;
 	}
 	if ((err) == LIBUSB_TRANSFER_CANCELLED)
 		err = LIBUSB_ERROR_TIMEOUT;
 out:
-	libusb_free_transfer(ut.transfer);
+	complete_usb_transfer(&ut);
 	return err;
 }
 
@@ -2817,8 +2855,8 @@ int __usb_transfer(struct cgpu_info *cgpu, uint8_t request_type, uint8_t bReques
 	}
 	STATS_TIMEVAL(&tv_start);
 	cg_rlock(&cgusb_fd_lock);
-	err = usb_control_transfer(usbdev->handle, request_type,
-		bRequest, wValue, wIndex, buf, (uint16_t)siz, timeout);
+	err = usb_control_transfer(cgpu, usbdev->handle, request_type, bRequest,
+				   wValue, wIndex, buf, (uint16_t)siz, timeout);
 	cg_runlock(&cgusb_fd_lock);
 	STATS_TIMEVAL(&tv_finish);
 	USB_STATS(cgpu, &tv_start, &tv_finish, err, MODE_CTRL_WRITE, cmd, SEQ0, timeout);
@@ -2828,7 +2866,7 @@ int __usb_transfer(struct cgpu_info *cgpu, uint8_t request_type, uint8_t bReques
 	IOERR_CHECK(cgpu, err);
 
 	if (err < 0 && err != LIBUSB_ERROR_TIMEOUT) {
-		applog(LOG_WARNING, "%s %i usb transfer error(%d): %s", cgpu->drv->name, cgpu->device_id,
+		applog(LOG_WARNING, "%s %i usb transfer err:(%d) %s", cgpu->drv->name, cgpu->device_id,
 		       err, libusb_error_name(err));
 	}
 out_:
@@ -2899,9 +2937,8 @@ int _usb_transfer_read(struct cgpu_info *cgpu, uint8_t request_type, uint8_t bRe
 	memset(tbuf, 0, 64);
 	STATS_TIMEVAL(&tv_start);
 	cg_rlock(&cgusb_fd_lock);
-	err = usb_control_transfer(usbdev->handle, request_type,
-		bRequest, wValue, wIndex,
-		tbuf, (uint16_t)bufsiz, timeout);
+	err = usb_control_transfer(cgpu, usbdev->handle, request_type, bRequest,
+				   wValue, wIndex, tbuf, (uint16_t)bufsiz, timeout);
 	cg_runlock(&cgusb_fd_lock);
 	STATS_TIMEVAL(&tv_finish);
 	USB_STATS(cgpu, &tv_start, &tv_finish, err, MODE_CTRL_READ, cmd, SEQ0, timeout);
@@ -2916,7 +2953,7 @@ int _usb_transfer_read(struct cgpu_info *cgpu, uint8_t request_type, uint8_t bRe
 		err = 0;
 	}
 	if (err < 0 && err != LIBUSB_ERROR_TIMEOUT) {
-		applog(LOG_WARNING, "%s %i usb transfer read error(%d): %s", cgpu->drv->name, cgpu->device_id,
+		applog(LOG_WARNING, "%s %i usb transfer read err:(%d) %s", cgpu->drv->name, cgpu->device_id,
 		       err, libusb_error_name(err));
 	}
 out_noerrmsg:

+ 1 - 0
usbutils.h

@@ -145,6 +145,7 @@ enum sub_ident {
 	IDENT_AVA,
 	IDENT_BTB,
 	IDENT_HFA,
+	IDENT_BBF,
 	IDENT_KLN,
 	IDENT_ICA,
 	IDENT_AMU,

+ 41 - 0
util.c

@@ -2485,3 +2485,44 @@ void _cgsem_destroy(cgsem_t *cgsem)
 	sem_destroy(cgsem);
 }
 #endif
+
+/* Provide a completion_timeout helper function for unreliable functions that
+ * may die due to driver issues etc that time out if the function fails and
+ * can then reliably return. */
+struct cg_completion {
+	cgsem_t cgsem;
+	void (*fn)(void *fnarg);
+	void *fnarg;
+};
+
+void *completion_thread(void *arg)
+{
+	struct cg_completion *cgc = (struct cg_completion *)arg;
+
+	pthread_detach(pthread_self());
+	cgc->fn(cgc->fnarg);
+	cgsem_post(&cgc->cgsem);
+
+	return NULL;
+}
+
+bool cg_completion_timeout(void *fn, void *fnarg, int timeout)
+{
+	struct cg_completion *cgc;
+	pthread_t pthread;
+	bool ret = false;
+
+	cgc = malloc(sizeof(struct cg_completion));
+	if (unlikely(!cgc))
+		return ret;
+	cgsem_init(&cgc->cgsem);
+	cgc->fn = fn;
+	cgc->fnarg = fnarg;
+
+	pthread_create(&pthread, NULL, completion_thread, (void *)cgc);
+
+	ret = cgsem_mswait(&cgc->cgsem, timeout);
+	if (!ret)
+		free(cgc);
+	return !ret;
+}

+ 1 - 0
util.h

@@ -135,6 +135,7 @@ void _cgsem_post(cgsem_t *cgsem, const char *file, const char *func, const int l
 void _cgsem_wait(cgsem_t *cgsem, const char *file, const char *func, const int line);
 int _cgsem_mswait(cgsem_t *cgsem, int ms, const char *file, const char *func, const int line);
 void _cgsem_destroy(cgsem_t *cgsem);
+bool cg_completion_timeout(void *fn, void *fnarg, int timeout);
 
 #define cgsem_init(_sem) _cgsem_init(_sem, __FILE__, __func__, __LINE__)
 #define cgsem_post(_sem) _cgsem_post(_sem, __FILE__, __func__, __LINE__)