Browse Source

cointerra: Import driver from cgminer as-is

cgminer commit: a3e3957809e358aeb2872f56a8e48db6cfc94051
Con Kolivas 12 years ago
parent
commit
d2d8ad53dd
2 changed files with 1480 additions and 0 deletions
  1. 1256 0
      driver-cointerra.c
  2. 224 0
      driver-cointerra.h

+ 1256 - 0
driver-cointerra.c

@@ -0,0 +1,1256 @@
+/*
+ * Copyright 2013-2014 Con Kolivas <kernel@kolivas.org>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 3 of the License, or (at your option)
+ * any later version.  See COPYING for more details.
+ */
+
+#include "config.h"
+
+#include "miner.h"
+#include "driver-cointerra.h"
+#include <math.h>
+
+static const char *cointerra_hdr = "ZZ";
+
+int opt_ps_load;
+
+static void cta_gen_message(char *msg, char type)
+{
+	memset(msg, 0, CTA_MSG_SIZE);
+	memcpy(msg, cointerra_hdr, 2);
+	msg[CTA_MSG_TYPE] = type;
+}
+
+/* Find the number of leading zero bits in diff */
+static uint8_t diff_to_bits(double diff)
+{
+	uint64_t diff64;
+	uint8_t i;
+
+	diff /= 0.9999847412109375;
+	diff *= (double)2147483648.0;
+	if (diff > 0x8000000000000000ULL)
+		diff = 0x8000000000000000ULL;
+	/* Convert it to an integer */
+	diff64 = diff;
+	for (i = 0; diff64; i++, diff64 >>= 1);
+
+	return i;
+}
+
+static double bits_to_diff(uint8_t bits)
+{
+	double ret = 1.0;
+
+	if (likely(bits > 32))
+		ret *= 1ull << (bits - 32);
+	else if (unlikely(bits < 32))
+		ret /= 1ull << (32 - bits);
+	return ret;
+}
+
+static bool cta_reset_init(char *buf)
+{
+	return ((buf[CTA_MSG_TYPE] == CTA_RECV_RDONE) && ((buf[CTA_RESET_TYPE]&0x3) == CTA_RESET_INIT));
+}
+
+static char *mystrstr(char *haystack, int size, const char *needle)
+{
+	int loop = 0;
+
+	while (loop < (size-1)) {
+		if ((haystack[loop] == needle[0])&&
+		    (haystack[loop+1] == needle[1]))
+			return &haystack[loop];
+		loop++;
+	}
+	return NULL;
+}
+
+static bool cta_open(struct cgpu_info *cointerra)
+{
+	int err, amount, offset = 0;
+	char buf[CTA_MSG_SIZE];
+	cgtimer_t ts_start;
+	bool ret = false;
+
+	if (cointerra->usbinfo.nodev)
+		return false;
+
+	applog(LOG_INFO, "CTA_OPEN");
+
+	cta_gen_message(buf, CTA_SEND_RESET);
+	// set the initial difficulty
+	buf[CTA_RESET_TYPE] = CTA_RESET_INIT | CTA_RESET_DIFF;
+	buf[CTA_RESET_DIFF] = diff_to_bits(CTA_INIT_DIFF);
+	buf[CTA_RESET_LOAD] = opt_cta_load ? opt_cta_load : 255;
+	buf[CTA_RESET_PSLOAD] = opt_ps_load;
+
+	if (cointerra->usbinfo.nodev)
+		return ret;
+
+	err = usb_write(cointerra, buf, CTA_MSG_SIZE, &amount, C_CTA_WRITE);
+	if (err) {
+		applog(LOG_INFO, "Write error %d, wrote %d of %d", err, amount, CTA_MSG_SIZE);
+		return ret;
+	}
+
+	cgtimer_time(&ts_start);
+
+	/* Read from the device for up to 2 seconds discarding any data that
+	 * doesn't match a reset complete acknowledgement. */
+	while (42) {
+		cgtimer_t ts_now, ts_diff;
+		char *msg;
+
+		cgtimer_time(&ts_now);
+		cgtimer_sub(&ts_now, &ts_start, &ts_diff);
+		if (cgtimer_to_ms(&ts_diff) > 2000) {
+			applog(LOG_DEBUG, "%s %d: Timed out waiting for response to reset init",
+			       cointerra->drv->name, cointerra->device_id);
+			break;
+		}
+
+		if (cointerra->usbinfo.nodev)
+			break;
+
+		err = usb_read(cointerra, buf + offset, CTA_MSG_SIZE - offset, &amount, C_CTA_READ);
+		if (err && err != LIBUSB_ERROR_TIMEOUT) {
+			applog(LOG_INFO, "%s %d: Read error %d, read %d", cointerra->drv->name,
+			       cointerra->device_id, err, amount);
+			break;
+		}
+		if (!amount)
+			continue;
+
+		msg = mystrstr(buf, amount, cointerra_hdr);
+		if (!msg) {
+			/* Keep the last byte in case it's the first byte of
+			 * the 2 byte header. */
+			offset = 1;
+			memmove(buf, buf + amount - 1, offset);
+			continue;
+		}
+
+		if (msg > buf) {
+			/* length of message = offset for next usb_read after moving */
+			offset = CTA_MSG_SIZE - (msg - buf);
+			memmove(buf, msg, offset);
+			continue;
+		}
+
+		/* We have a full sized message starting with the header now */
+		if (cta_reset_init(buf)) {
+			/* We can't store any other data returned with this
+			 * reset since we have not allocated any memory for
+			 * a cointerra_info structure yet. */
+			applog(LOG_INFO, "%s %d: Successful reset init received",
+			       cointerra->drv->name, cointerra->device_id);
+			ret = true;
+			break;
+		}
+	}
+
+	return ret;
+}
+
+static void cta_clear_work(struct cgpu_info *cgpu)
+{
+	struct work *work, *tmp;
+
+	wr_lock(&cgpu->qlock);
+	HASH_ITER(hh, cgpu->queued_work, work, tmp) {
+		__work_completed(cgpu, work);
+		free_work(work);
+	}
+	wr_unlock(&cgpu->qlock);
+}
+
+static void cta_close(struct cgpu_info *cointerra)
+{
+	struct cointerra_info *info = cointerra->device_data;
+
+	/* Wait for read thread to die */
+	pthread_join(info->read_thr, NULL);
+
+	/* Open does the same reset init followed by response as is required to
+	 * close the device. */
+	if (!cta_open(cointerra)) {
+		applog(LOG_INFO, "%s %d: Reset on close failed", cointerra->drv->name,
+			cointerra->device_id);
+	}
+
+	mutex_destroy(&info->lock);
+	mutex_destroy(&info->sendlock);
+	/* Don't free info here to avoid trying to access dereferenced members
+	 * once a device is unplugged. */
+	cta_clear_work(cointerra);
+}
+
+static struct cgpu_info *cta_detect_one(struct libusb_device *dev, struct usb_find_devices *found)
+{
+	struct cgpu_info *cointerra = usb_alloc_cgpu(&cointerra_drv, 1);
+	int tries = 0;
+
+	if (!usb_init(cointerra, dev, found))
+		goto fail;
+	applog(LOG_INFO, "%s %d: Found at %s", cointerra->drv->name,
+	       cointerra->device_id, cointerra->device_path);
+
+	while (!cta_open(cointerra) && !cointerra->usbinfo.nodev) {
+		if (tries++ > 3)
+			goto failed_open;
+		applog(LOG_INFO, "%s %d: Failed to open %d times, retrying", cointerra->drv->name,
+		       cointerra->device_id, tries);
+	}
+
+	if (!add_cgpu(cointerra))
+		goto fail_close;
+
+	update_usb_stats(cointerra);
+	applog(LOG_INFO, "%s %d: Successfully set up %s", cointerra->drv->name,
+	       cointerra->device_id, cointerra->device_path);
+	return cointerra;
+
+fail_close:
+	cta_close(cointerra);
+failed_open:
+	applog(LOG_INFO, "%s %d: Failed to initialise %s", cointerra->drv->name,
+	       cointerra->device_id, cointerra->device_path);
+fail:
+	usb_free_cgpu(cointerra);
+	return NULL;
+}
+
+static void cta_detect(bool __maybe_unused hotplug)
+{
+	usb_detect(&cointerra_drv, cta_detect_one);
+}
+
+/* This function will remove a work item from the hashtable if it matches the
+ * id in work->subid and return a pointer to the work but it will not free the
+ * work. It may return NULL if it cannot find matching work. */
+static struct work *take_work_by_id(struct cgpu_info *cgpu, uint16_t id)
+{
+	struct work *work, *tmp, *ret = NULL;
+
+	wr_lock(&cgpu->qlock);
+	HASH_ITER(hh, cgpu->queued_work, work, tmp) {
+		if (work->subid == id) {
+			ret = work;
+			break;
+		}
+	}
+	if (ret)
+		__work_completed(cgpu, ret);
+	wr_unlock(&cgpu->qlock);
+
+	return ret;
+}
+
+/* This function will look up a work item in the hashtable if it matches the
+ * id in work->subid and return a cloned work item if it matches. It may return
+ * NULL if it cannot find matching work. */
+static struct work *clone_work_by_id(struct cgpu_info *cgpu, uint16_t id)
+{
+	struct work *work, *tmp, *ret = NULL;
+
+	rd_lock(&cgpu->qlock);
+	HASH_ITER(hh, cgpu->queued_work, work, tmp) {
+		if (work->subid == id) {
+			ret = work;
+			break;
+		}
+	}
+	if (ret)
+		ret = copy_work(ret);
+	rd_unlock(&cgpu->qlock);
+
+	return ret;
+}
+
+static bool cta_send_msg(struct cgpu_info *cointerra, char *buf);
+
+static uint16_t hu16_from_msg(char *buf, int msg)
+{
+	return le16toh(*(uint16_t *)&buf[msg]);
+}
+
+static uint32_t hu32_from_msg(char *buf, int msg)
+{
+	return le32toh(*(uint32_t *)&buf[msg]);
+}
+
+static uint64_t hu64_from_msg(char *buf, int msg)
+{
+	return le64toh(*(uint64_t *)&buf[msg]);
+}
+
+static uint8_t u8_from_msg(char *buf, int msg)
+{
+	return *(uint8_t *)&buf[msg];
+}
+
+static void msg_from_hu16(char *buf, int msg, uint16_t val)
+{
+	*(uint16_t *)&buf[msg] = htole16(val);
+}
+
+static void cta_parse_reqwork(struct cgpu_info *cointerra, struct cointerra_info *info,
+			      char *buf)
+{
+	uint16_t retwork;
+
+	retwork = hu16_from_msg(buf, CTA_REQWORK_REQUESTS);
+	applog(LOG_DEBUG, "%s %d: Request work message for %u items received",
+	       cointerra->drv->name, cointerra->device_id, retwork);
+
+	mutex_lock(&info->lock);
+	info->requested = retwork;
+	/* Wake up the main scanwork loop since we need more
+		* work. */
+	pthread_cond_signal(&info->wake_cond);
+	mutex_unlock(&info->lock);
+}
+
+static void cta_parse_recvmatch(struct thr_info *thr, struct cgpu_info *cointerra,
+				struct cointerra_info *info, char *buf)
+{
+	uint32_t timestamp_offset, mcu_tag;
+	uint16_t retwork;
+	struct work *work;
+
+	/* No endian switch needs doing here since it's sent and returned as
+	 * the same 4 bytes */
+	retwork = *(uint16_t *)(&buf[CTA_DRIVER_TAG]);
+	mcu_tag = hu32_from_msg(buf, CTA_MCU_TAG);
+	applog(LOG_DEBUG, "%s %d: Match message for id 0x%04x MCU id 0x%08x received",
+	       cointerra->drv->name, cointerra->device_id, retwork, mcu_tag);
+
+	work = clone_work_by_id(cointerra, retwork);
+	if (likely(work)) {
+		uint8_t wdiffbits = u8_from_msg(buf, CTA_WORK_DIFFBITS);
+		uint32_t nonce = hu32_from_msg(buf, CTA_MATCH_NONCE);
+		unsigned char rhash[32];
+		char outhash[16];
+		double wdiff;
+		bool ret;
+
+		timestamp_offset = hu32_from_msg(buf, CTA_MATCH_NOFFSET);
+		if (timestamp_offset) {
+			struct work *base_work = work;
+
+			work = copy_work_noffset(base_work, timestamp_offset);
+			free_work(base_work);
+		}
+
+		/* Test against the difficulty we asked for along with the work */
+		wdiff = bits_to_diff(wdiffbits);
+		ret = test_nonce_diff(work, nonce, wdiff);
+
+		if (opt_debug) {
+			/* Debugging, remove me */
+			swab256(rhash, work->hash);
+			__bin2hex(outhash, rhash, 8);
+			applog(LOG_DEBUG, "submit work %s 0x%04x 0x%08x %d 0x%08x",
+			       outhash, retwork, mcu_tag, timestamp_offset, nonce);
+		}
+
+		if (likely(ret)) {
+			uint8_t asic, core, pipe, coreno;
+			int pipeno, bitchar, bitbit;
+			uint64_t hashes;
+
+			asic = u8_from_msg(buf, CTA_MCU_ASIC);
+			core = u8_from_msg(buf, CTA_MCU_CORE);
+			pipe = u8_from_msg(buf, CTA_MCU_PIPE);
+			pipeno = asic * 512 + core * 128 + pipe;
+			coreno = asic * 4 + core;
+			if (unlikely(asic > 1 || core > 3 || pipe > 127 || pipeno > 1023)) {
+				applog(LOG_WARNING, "%s %d: MCU invalid pipe asic %d core %d pipe %d",
+				       cointerra->drv->name, cointerra->device_id, asic, core, pipe);
+				coreno = 0;
+			} else {
+				info->last_pipe_nonce[pipeno] = time(NULL);
+				bitchar = pipeno / 8;
+				bitbit = pipeno % 8;
+				info->pipe_bitmap[bitchar] |= 0x80 >> bitbit;
+			}
+
+			applog(LOG_DEBUG, "%s %d: Submitting tested work job_id %s work_id %u",
+			       cointerra->drv->name, cointerra->device_id, work->job_id, work->subid);
+			ret = submit_tested_work(thr, work);
+
+			hashes = (uint64_t)wdiff * 0x100000000ull;
+			mutex_lock(&info->lock);
+			info->share_hashes += hashes;
+			info->tot_core_hashes[coreno] += hashes;
+			info->hashes += nonce;
+			mutex_unlock(&info->lock);
+		} else {
+			char sendbuf[CTA_MSG_SIZE];
+
+			applog(LOG_DEBUG, "%s %d: Notify bad match work",
+			       cointerra->drv->name, cointerra->device_id);
+			if (opt_debug) {
+				uint64_t sdiff = share_diff(work);
+				unsigned char midstate[32], wdata[12];
+				char hexmidstate[68], hexwdata[28];
+				uint16_t wid;
+
+				memcpy(&wid, &info->work_id, 2);
+				flip32(midstate, work->midstate);
+				__bin2hex(hexmidstate, midstate, 32);
+				flip12(wdata, &work->data[64]);
+				__bin2hex(hexwdata, wdata, 12);
+				applog(LOG_DEBUG, "False match sent: work id %u midstate %s  blkhdr %s",
+				       wid, hexmidstate, hexwdata);
+				applog(LOG_DEBUG, "False match reports: work id 0x%04x MCU id 0x%08x work diff %.1f",
+				       retwork, mcu_tag, wdiff);
+				applog(LOG_DEBUG, "False match tested: nonce 0x%08x noffset %d %s",
+				       nonce, timestamp_offset, outhash);
+				applog(LOG_DEBUG, "False match devdiff set to %.1f share diff calc %"PRIu64,
+				       work->device_diff, sdiff);
+			}
+
+			/* Tell the device we got a false match */
+			cta_gen_message(sendbuf, CTA_SEND_FMATCH);
+			memcpy(sendbuf + 3, buf, CTA_MSG_SIZE - 3);
+			cta_send_msg(cointerra, sendbuf);
+		}
+		free_work(work);
+	} else {
+		applog(LOG_INFO, "%s %d: Matching work id 0x%X %d not found", cointerra->drv->name,
+		       cointerra->device_id, retwork, __LINE__);
+		inc_hw_errors(thr);
+
+		mutex_lock(&info->lock);
+		info->no_matching_work++;
+		mutex_unlock(&info->lock);
+	}
+}
+
+static void cta_parse_wdone(struct thr_info *thr, struct cgpu_info *cointerra,
+			    struct cointerra_info *info, char *buf)
+{
+	uint16_t retwork = *(uint16_t *)(&buf[CTA_DRIVER_TAG]);
+	struct work *work = take_work_by_id(cointerra, retwork);
+	uint64_t hashes;
+
+	if (likely(work))
+		free_work(work);
+	else {
+		applog(LOG_INFO, "%s %d: Done work not found id 0x%X %d",
+		       cointerra->drv->name, cointerra->device_id, retwork, __LINE__);
+		inc_hw_errors(thr);
+	}
+
+	/* Removing hashes from work done message */
+	hashes = hu64_from_msg(buf, CTA_WDONE_NONCES);
+	if (unlikely(hashes > (61 * 0x100000000ull))) {
+		applog(LOG_INFO, "%s Invalid hash returned %"PRIu64"x %"PRIu64"x %"PRIu64"X",
+		       __func__, info->hashes, hashes, hashes);
+		hashes = 0;
+	}
+
+	mutex_lock(&info->lock);
+	info->hashes += hashes;
+	mutex_unlock(&info->lock);
+}
+
+static void u16array_from_msg(uint16_t *u16, int entries, int var, char *buf)
+{
+	int i, j;
+
+	for (i = 0, j = 0; i < entries; i++, j += sizeof(uint16_t))
+		u16[i] = hu16_from_msg(buf, var + j);
+}
+
+static void cta_parse_statread(struct cgpu_info *cointerra, struct cointerra_info *info,
+			       char *buf)
+{
+	float max_temp = 0;
+	int i;
+
+	mutex_lock(&info->lock);
+	u16array_from_msg(info->coretemp, CTA_CORES, CTA_STAT_CORETEMPS, buf);
+	info->ambtemp_low = hu16_from_msg(buf, CTA_STAT_AMBTEMP_LOW);
+	info->ambtemp_avg = hu16_from_msg(buf, CTA_STAT_AMBTEMP_AVG);
+	info->ambtemp_high = hu16_from_msg(buf, CTA_STAT_AMBTEMP_HIGH);
+	u16array_from_msg(info->pump_tachs, CTA_PUMPS, CTA_STAT_PUMP_TACHS, buf);
+	u16array_from_msg(info->fan_tachs, CTA_FANS, CTA_STAT_FAN_TACHS, buf);
+	u16array_from_msg(info->corevolts, CTA_CORES, CTA_STAT_CORE_VOLTS, buf);
+	info->volts33 = hu16_from_msg(buf, CTA_STAT_VOLTS33);
+	info->volts12 = hu16_from_msg(buf, CTA_STAT_VOLTS12);
+	info->inactive = hu16_from_msg(buf, CTA_STAT_INACTIVE);
+	info->active = hu16_from_msg(buf, CTA_STAT_ACTIVE);
+	mutex_unlock(&info->lock);
+
+	for (i = 0; i < CTA_CORES; i++) {
+		if (info->coretemp[i] > max_temp)
+			max_temp = info->coretemp[i];
+	}
+	max_temp /= 100.0;
+	/* Store the max temperature in the cgpu struct as an exponentially
+	 * changing value. */
+	cointerra->temp = cointerra->temp * 0.63 + max_temp * 0.37;
+}
+
+static void u8array_from_msg(uint8_t *u8, int entries, int var, char *buf)
+{
+	int i;
+
+	for (i = 0; i < entries; i++)
+		u8[i] = u8_from_msg(buf, var + i);
+}
+
+static void cta_parse_statset(struct cointerra_info *info, char *buf)
+{
+	mutex_lock(&info->lock);
+	u8array_from_msg(info->coreperf, CTA_CORES, CTA_STAT_PERFMODE, buf);
+	u8array_from_msg(info->fanspeed, CTA_FANS, CTA_STAT_FANSPEEDS, buf);
+	info->dies_active = u8_from_msg(buf, CTA_STAT_DIES_ACTIVE);
+	u8array_from_msg(info->pipes_enabled, CTA_CORES, CTA_STAT_PIPES_ENABLED, buf);
+	u16array_from_msg(info->corefreqs, CTA_CORES, CTA_STAT_CORE_FREQS, buf);
+	info->uptime = hu32_from_msg(buf,CTA_STAT_UPTIME);
+	mutex_unlock(&info->lock);
+}
+
+static void cta_parse_info(struct cgpu_info *cointerra, struct cointerra_info *info,
+			   char *buf)
+{
+	mutex_lock(&info->lock);
+	info->hwrev = hu64_from_msg(buf, CTA_INFO_HWREV);
+	info->serial = hu32_from_msg(buf, CTA_INFO_SERNO);
+	info->asics = u8_from_msg(buf, CTA_INFO_NUMASICS);
+	info->dies = u8_from_msg(buf, CTA_INFO_NUMDIES);
+	info->cores = hu16_from_msg(buf, CTA_INFO_NUMCORES);
+	info->board_number = u8_from_msg(buf, CTA_INFO_BOARDNUMBER);
+	info->fwrev[0] = u8_from_msg(buf, CTA_INFO_FWREV_MAJ);
+	info->fwrev[1] = u8_from_msg(buf, CTA_INFO_FWREV_MIN);
+	info->fwrev[2] = u8_from_msg(buf, CTA_INFO_FWREV_MIC);
+	info->fw_year = hu16_from_msg(buf, CTA_INFO_FWDATE_YEAR);
+	info->fw_month = u8_from_msg(buf, CTA_INFO_FWDATE_MONTH);
+	info->fw_day = u8_from_msg(buf, CTA_INFO_FWDATE_DAY);
+	info->init_diffbits = u8_from_msg(buf, CTA_INFO_INITDIFFBITS);
+	info->min_diffbits = u8_from_msg(buf, CTA_INFO_MINDIFFBITS);
+	info->max_diffbits = u8_from_msg(buf, CTA_INFO_MAXDIFFBITS);
+	mutex_unlock(&info->lock);
+
+	if (!cointerra->unique_id) {
+		uint32_t b32 = htobe32(info->serial);
+
+		cointerra->unique_id = bin2hex((unsigned char *)&b32, 4);
+	}
+}
+
+static void cta_parse_rdone(struct cgpu_info *cointerra, struct cointerra_info *info,
+			    char *buf)
+{
+	uint8_t reset_type, diffbits;
+	uint64_t wdone;
+
+	reset_type = buf[CTA_RESET_TYPE];
+	diffbits = buf[CTA_RESET_DIFF];
+	wdone = hu64_from_msg(buf, CTA_WDONE_NONCES);
+
+	applog(LOG_INFO, "%s %d: Reset done type %u message %u diffbits %"PRIu64" done received",
+	       cointerra->drv->name, cointerra->device_id, reset_type, diffbits, wdone);
+	if (wdone) {
+		applog(LOG_INFO, "%s %d: Reset done type %u message %u diffbits %"PRIu64" done received",
+			cointerra->drv->name, cointerra->device_id, reset_type, diffbits, wdone);
+
+		mutex_lock(&info->lock);
+		info->hashes += wdone;
+		mutex_unlock(&info->lock);
+	}
+
+	/* Note that the cgsem that is posted here must not be waited on while
+	 * holding the info->lock to not get into a livelock since this
+	 * function also grabs the lock first and it's always best to not sleep
+	 * while holding a lock. */
+	if (reset_type == CTA_RESET_NEW) {
+		cta_clear_work(cointerra);
+		/* Tell reset sender that the reset is complete
+			* and it may resume. */
+		cgsem_post(&info->reset_sem);
+	}
+}
+
+static void cta_zero_stats(struct cgpu_info *cointerra);
+
+static void cta_parse_debug(struct cointerra_info *info, char *buf)
+{
+	mutex_lock(&info->lock);
+
+	info->tot_underruns = hu16_from_msg(buf, CTA_STAT_UNDERRUNS);
+	u16array_from_msg(info->tot_hw_errors, CTA_CORES, CTA_STAT_HW_ERRORS, buf);
+	info->tot_hashes = hu64_from_msg(buf, CTA_STAT_HASHES);
+	info->tot_flushed_hashes = hu64_from_msg(buf, CTA_STAT_FLUSHED_HASHES);
+	info->autovoltage = u8_from_msg(buf, CTA_STAT_AUTOVOLTAGE);
+	info->current_ps_percent = u8_from_msg(buf, CTA_STAT_POWER_PERCENT);
+	info->power_used = hu16_from_msg(buf,CTA_STAT_POWER_USED);
+	info->power_voltage = hu16_from_msg(buf,CTA_STAT_VOLTAGE);
+	info->ipower_used = hu16_from_msg(buf,CTA_STAT_IPOWER_USED);
+	info->ipower_voltage = hu16_from_msg(buf,CTA_STAT_IVOLTAGE);
+	info->power_temps[0] = hu16_from_msg(buf,CTA_STAT_PS_TEMP1);
+	info->power_temps[1] = hu16_from_msg(buf,CTA_STAT_PS_TEMP2);
+
+	mutex_unlock(&info->lock);
+
+	/* Autovoltage is positive only once at startup and eventually drops
+	 * to zero. After that time we reset the stats since they're unreliable
+	 * till then. */
+	if (unlikely(!info->autovoltage_complete && !info->autovoltage)) {
+		struct cgpu_info *cointerra = info->thr->cgpu;
+
+		info->autovoltage_complete = true;
+		cgtime(&cointerra->dev_start_tv);
+		cta_zero_stats(cointerra);
+		cointerra->total_mhashes = 0;
+		cointerra->accepted = 0;
+		cointerra->rejected = 0;
+		cointerra->hw_errors = 0;
+		cointerra->utility = 0.0;
+		cointerra->last_share_pool_time = 0;
+		cointerra->diff1 = 0;
+		cointerra->diff_accepted = 0;
+		cointerra->diff_rejected = 0;
+		cointerra->last_share_diff = 0;
+	}
+}
+
+static void cta_parse_msg(struct thr_info *thr, struct cgpu_info *cointerra,
+			  struct cointerra_info *info, char *buf)
+{
+	switch (buf[CTA_MSG_TYPE]) {
+		default:
+		case CTA_RECV_UNUSED:
+			applog(LOG_INFO, "%s %d: Unidentified message type %u",
+			       cointerra->drv->name, cointerra->device_id, buf[CTA_MSG_TYPE]);
+			break;
+		case CTA_RECV_REQWORK:
+			cta_parse_reqwork(cointerra, info, buf);
+			break;
+		case CTA_RECV_MATCH:
+			cta_parse_recvmatch(thr, cointerra, info, buf);
+			break;
+		case CTA_RECV_WDONE:
+			applog(LOG_DEBUG, "%s %d: Work done message received",
+			       cointerra->drv->name, cointerra->device_id);
+			cta_parse_wdone(thr, cointerra, info, buf);
+			break;
+		case CTA_RECV_STATREAD:
+			applog(LOG_DEBUG, "%s %d: Status readings message received",
+			       cointerra->drv->name, cointerra->device_id);
+			cta_parse_statread(cointerra, info, buf);
+			break;
+		case CTA_RECV_STATSET:
+			applog(LOG_DEBUG, "%s %d: Status settings message received",
+			       cointerra->drv->name, cointerra->device_id);
+			cta_parse_statset(info, buf);
+			break;
+		case CTA_RECV_INFO:
+			applog(LOG_DEBUG, "%s %d: Info message received",
+			       cointerra->drv->name, cointerra->device_id);
+			cta_parse_info(cointerra, info, buf);
+			break;
+		case CTA_RECV_MSG:
+			applog(LOG_NOTICE, "%s %d: MSG: %s",
+			       cointerra->drv->name, cointerra->device_id, &buf[CTA_MSG_RECVD]);
+			break;
+		case CTA_RECV_RDONE:
+			cta_parse_rdone(cointerra, info, buf);
+			break;
+		case CTA_RECV_STATDEBUG:
+			cta_parse_debug(info, buf);
+			break;
+	}
+}
+
+static void *cta_recv_thread(void *arg)
+{
+	struct thr_info *thr = (struct thr_info *)arg;
+	struct cgpu_info *cointerra = thr->cgpu;
+	struct cointerra_info *info = cointerra->device_data;
+	char threadname[24];
+	int offset = 0;
+
+	snprintf(threadname, 24, "cta_recv/%d", cointerra->device_id);
+	RenameThread(threadname);
+
+	while (likely(!cointerra->shutdown)) {
+		char buf[CTA_READBUF_SIZE];
+		int amount, err;
+
+		if (unlikely(cointerra->usbinfo.nodev)) {
+			applog(LOG_DEBUG, "%s %d: Device disappeared, disabling recv thread",
+			       cointerra->drv->name, cointerra->device_id);
+			break;
+		}
+
+		err = usb_read(cointerra, buf + offset, CTA_MSG_SIZE, &amount, C_CTA_READ);
+		if (err && err != LIBUSB_ERROR_TIMEOUT) {
+			applog(LOG_ERR, "%s %d: Read error %d, read %d", cointerra->drv->name,
+			       cointerra->device_id, err, amount);
+			break;
+		}
+		offset += amount;
+
+		while (offset >= CTA_MSG_SIZE) {
+			char *msg = mystrstr(buf, offset, cointerra_hdr);
+			int begin;
+
+			if (unlikely(!msg)) {
+				applog(LOG_WARNING, "%s %d: No message header found, discarding buffer",
+				       cointerra->drv->name, cointerra->device_id);
+				inc_hw_errors(thr);
+				/* Save the last byte in case it's the fist
+				 * byte of a header. */
+				begin = CTA_MSG_SIZE - 1;
+				offset -= begin;
+				memmove(buf, buf + begin, offset);
+				continue;
+			}
+
+			if (unlikely(msg != buf)) {
+				begin = msg - buf;
+				applog(LOG_WARNING, "%s %d: Reads out of sync, discarding %d bytes",
+				       cointerra->drv->name, cointerra->device_id, begin);
+				inc_hw_errors(thr);
+				offset -= begin;
+				memmove(buf, msg, offset);
+				if (offset < CTA_MSG_SIZE)
+					break;
+			}
+
+			/* We have enough buffer for a full message, parse now */
+			cta_parse_msg(thr, cointerra, info, msg);
+			offset -= CTA_MSG_SIZE;
+			if (offset > 0)
+				memmove(buf, buf + CTA_MSG_SIZE, offset);
+		}
+	}
+
+	return NULL;
+}
+
+static bool cta_send_msg(struct cgpu_info *cointerra, char *buf)
+{
+	struct cointerra_info *info = cointerra->device_data;
+	int amount, err;
+
+	if (unlikely(cointerra->usbinfo.nodev))
+		return false;
+
+	/* Serialise usb writes to prevent overlap in case multiple threads
+	 * send messages */
+	mutex_lock(&info->sendlock);
+	err = usb_write(cointerra, buf, CTA_MSG_SIZE, &amount, C_CTA_WRITE);
+	mutex_unlock(&info->sendlock);
+
+	if (unlikely(err || amount != CTA_MSG_SIZE)) {
+		applog(LOG_ERR, "%s %d: Write error %d, wrote %d of %d", cointerra->drv->name,
+		       cointerra->device_id, err, amount, CTA_MSG_SIZE);
+		return false;
+	}
+	return true;
+}
+
+static bool cta_prepare(struct thr_info *thr)
+{
+	struct cgpu_info *cointerra = thr->cgpu;
+	struct cointerra_info *info = calloc(sizeof(struct cointerra_info), 1);
+	char buf[CTA_MSG_SIZE];
+
+	if (unlikely(cointerra->usbinfo.nodev))
+		return false;
+
+	if (unlikely(!info))
+		quit(1, "Failed to calloc info in cta_detect_one");
+	cointerra->device_data = info;
+	/* Nominally set a requested value when starting, preempting the need
+	 * for a req-work message. */
+	info->requested = CTA_MAX_QUEUE;
+
+	info->thr = thr;
+	mutex_init(&info->lock);
+	mutex_init(&info->sendlock);
+	if (unlikely(pthread_cond_init(&info->wake_cond, NULL)))
+		quit(1, "Failed to create cta pthread cond");
+	cgsem_init(&info->reset_sem);
+	if (pthread_create(&info->read_thr, NULL, cta_recv_thread, (void *)thr))
+		quit(1, "Failed to create cta_recv_thread");
+
+	/* Request a single status setting message */
+	cta_gen_message(buf, CTA_SEND_REQUEST);
+	msg_from_hu16(buf, CTA_REQ_MSGTYPE, CTA_RECV_STATSET);
+	msg_from_hu16(buf, CTA_REQ_INTERVAL, 0);
+	if (!cta_send_msg(cointerra, buf))
+		return false;
+
+	/* Request status debug messages every 60 seconds */
+	cta_gen_message(buf, CTA_SEND_REQUEST);
+	msg_from_hu16(buf, CTA_REQ_MSGTYPE, CTA_RECV_STATDEBUG);
+	msg_from_hu16(buf, CTA_REQ_INTERVAL, 6000);
+	if (!cta_send_msg(cointerra, buf))
+		return false;
+
+	cgtime(&info->core_hash_start);
+
+	return true;
+}
+
+static void cta_send_reset(struct cgpu_info *cointerra, struct cointerra_info *info,
+			   uint8_t reset_type, uint8_t diffbits);
+static void cta_flush_work(struct cgpu_info *cointerra);
+
+/* *_fill and *_scanwork are serialised wrt to each other */
+static bool cta_fill(struct cgpu_info *cointerra)
+{
+	struct cointerra_info *info = cointerra->device_data;
+	bool ret = true;
+	char buf[CTA_MSG_SIZE];
+	struct work *work = NULL;
+	unsigned short nroll_limit;
+	uint32_t swab[8];
+	uint8_t diffbits;
+
+	//applog(LOG_WARNING, "%s %d: cta_fill %d", cointerra->drv->name, cointerra->device_id,__LINE__);
+
+	if (unlikely(info->thr->work_restart))
+		cta_flush_work(cointerra);
+
+	mutex_lock(&info->lock);
+	if (!info->requested)
+		goto out_unlock;
+	work = get_queued(cointerra);
+	if (unlikely(!work)) {
+		ret = false;
+		goto out_unlock;
+	}
+	if (--info->requested > 0)
+		ret = false;
+
+	/* It does not matter what endian this uint16_t is since it will be
+	 * the same value on sending to the MC as returning in match/done. This
+	 * will automatically wrap as a uint16_t. It cannot be zero for the MCU
+	 * though. */
+	if (unlikely(++info->work_id == 0))
+		info->work_id = 1;
+	work->subid = info->work_id;
+
+	diffbits = diff_to_bits(work->device_diff);
+
+	cta_gen_message(buf, CTA_SEND_WORK);
+
+ 	memcpy(buf + CTA_DRIVER_TAG, &info->work_id, 2);
+
+	flip32(swab, work->midstate);
+	memcpy(buf + CTA_WORK_MIDSTATE, swab, 32);
+
+	flip12(swab, &work->data[64]);
+	memcpy(buf + CTA_WORK_DATA, swab, 12);
+
+	nroll_limit = htole16(work->drv_rolllimit);
+	memcpy(buf + CTA_WORK_NROLL, &nroll_limit, 2);
+
+	memcpy(buf + CTA_WORK_DIFFBITS, &diffbits, 1);
+
+out_unlock:
+	mutex_unlock(&info->lock);
+
+	if (work) {
+		cgtime(&work->tv_work_start);
+		applog(LOG_DEBUG, "%s %d: Sending work job_id %s work_id %u", cointerra->drv->name,
+		       cointerra->device_id, work->job_id, work->subid);
+		if (unlikely(!cta_send_msg(cointerra, buf))) {
+			work_completed(cointerra, work);
+			applog(LOG_INFO, "%s %d: Failed to send work",
+			       cointerra->drv->name, cointerra->device_id);
+			/* The device will fail after this */
+		}
+	}
+
+	return ret;
+}
+
+static void cta_send_reset(struct cgpu_info *cointerra, struct cointerra_info *info,
+			   uint8_t reset_type, uint8_t diffbits)
+{
+	char buf[CTA_MSG_SIZE];
+	int ret, retries = 0;
+
+	/* Clear any accumulated messages in case we've gotten out of sync. */
+	cgsem_reset(&info->reset_sem);
+resend:
+	cta_gen_message(buf, CTA_SEND_RESET);
+
+	buf[CTA_RESET_TYPE] = reset_type;
+	buf[CTA_RESET_LOAD] = opt_cta_load ? opt_cta_load : 255;
+	buf[CTA_RESET_PSLOAD] = opt_ps_load;
+
+	applog(LOG_INFO, "%s %d: Sending Reset type %u with diffbits %u", cointerra->drv->name,
+	       cointerra->device_id, reset_type, diffbits);
+	cta_send_msg(cointerra, buf);
+
+	/* Wait for read thread to parse a reset message and signal us we may
+	 * return to submitting other messages. Use a timeout in case we have
+	 * a problem and the reset done message never returns. */
+	if (reset_type == CTA_RESET_NEW) {
+		ret = cgsem_mswait(&info->reset_sem, CTA_RESET_TIMEOUT);
+		if (ret) {
+			if (++retries < 3) {
+				applog(LOG_INFO, "%s %d: Timed out waiting for reset done msg, retrying",
+				       cointerra->drv->name, cointerra->device_id);
+				goto resend;
+			}
+			applog(LOG_WARNING, "%s %d: Timed out waiting for reset done msg",
+			       cointerra->drv->name, cointerra->device_id);
+		}
+		/* Good place to flush any work we have */
+		flush_queue(cointerra);
+	}
+}
+
+static void cta_flush_work(struct cgpu_info *cointerra)
+{
+	struct cointerra_info *info = cointerra->device_data;
+
+	applog(LOG_INFO, "%s %d: cta_flush_work %d", cointerra->drv->name, cointerra->device_id,
+	       __LINE__);
+	cta_send_reset(cointerra, info, CTA_RESET_NEW, 0);
+	info->thr->work_restart = false;
+}
+
+static void cta_update_work(struct cgpu_info *cointerra)
+{
+	struct cointerra_info *info = cointerra->device_data;
+
+	applog(LOG_INFO, "%s %d: Update work", cointerra->drv->name, cointerra->device_id);
+	cta_send_reset(cointerra, info, CTA_RESET_UPDATE, 0);
+}
+
+static void cta_zero_corehashes(struct cointerra_info *info)
+{
+	int i;
+
+	for (i = 0; i < CTA_CORES; i++)
+		info->tot_core_hashes[i] = 0;
+	cgtime(&info->core_hash_start);
+}
+
+/* Send per core hashrate calculations at regular intervals ~every 5 minutes */
+static void cta_send_corehashes(struct cgpu_info *cointerra, struct cointerra_info *info,
+				double corehash_time)
+{
+	uint16_t core_ghs[CTA_CORES];
+	double k[CTA_CORES];
+	char buf[CTA_MSG_SIZE];
+	int i, offset;
+
+	for (i = 0; i < CTA_CORES; i++) {
+		k[i] = (double)info->tot_core_hashes[i] / ((double)32 * (double)0x100000000ull);
+		k[i] = sqrt(k[i]) + 1;
+		k[i] *= k[i];
+		k[i] = k[i] * 32 * ((double)0x100000000ull / (double)1000000000) / corehash_time;
+		core_ghs[i] = k[i];
+	}
+	cta_gen_message(buf, CTA_SEND_COREHASHRATE);
+	offset = CTA_CORE_HASHRATES;
+	for (i = 0; i < CTA_CORES; i++) {
+		msg_from_hu16(buf, offset, core_ghs[i]);
+		offset += 2; // uint16_t
+	}
+	cta_send_msg(cointerra, buf);
+}
+
+static int64_t cta_scanwork(struct thr_info *thr)
+{
+	struct cgpu_info *cointerra = thr->cgpu;
+	struct cointerra_info *info = cointerra->device_data;
+	double corehash_time;
+	struct timeval now;
+	int64_t hashes;
+
+	applog(LOG_DEBUG, "%s %d: cta_scanwork %d", cointerra->drv->name, cointerra->device_id,__LINE__);
+
+	if (unlikely(cointerra->usbinfo.nodev)) {
+		hashes = -1;
+		goto out;
+	}
+
+	cgtime(&now);
+
+	if (unlikely(thr->work_restart)) {
+		applog(LOG_INFO, "%s %d: Flush work line %d",
+		     cointerra->drv->name, cointerra->device_id,__LINE__);
+		cta_flush_work(cointerra);
+	} else {
+		struct timespec abstime, tsdiff = {0, 500000000};
+		time_t now_t;
+		int i;
+
+		timeval_to_spec(&abstime, &now);
+		timeraddspec(&abstime, &tsdiff);
+
+		/* Discard work that was started more than 5 minutes ago as
+		 * a safety precaution backup in case the hardware failed to
+		 * return a work done message for some work items. */
+		age_queued_work(cointerra, 300.0);
+
+		/* Each core should be 1.7MH so at max diff of 32 should
+		 * average a share every ~80 seconds.Use this opportunity to
+		 * unset the bits in any pipes that have not returned a valid
+		 * nonce for over 30 full nonce ranges or 2400s. */
+		now_t = time(NULL);
+		for (i = 0; i < 1024; i++) {
+			if (unlikely(now_t > info->last_pipe_nonce[i] + 2400)) {
+				int bitchar = i / 8, bitbit = i % 8;
+
+				info->pipe_bitmap[bitchar] &= ~(0x80 >> bitbit);
+			}
+		}
+
+		/* Sleep for up to 0.5 seconds, waking if we need work or
+		 * have received a restart message. */
+		mutex_lock(&info->lock);
+		pthread_cond_timedwait(&info->wake_cond, &info->lock, &abstime);
+		mutex_unlock(&info->lock);
+
+		if (thr->work_restart) {
+			applog(LOG_INFO, "%s %d: Flush work line %d",
+			       cointerra->drv->name, cointerra->device_id,__LINE__);
+			cta_flush_work(cointerra);
+		}
+	}
+
+	corehash_time = tdiff(&now, &info->core_hash_start);
+	if (corehash_time > 300) {
+		cta_send_corehashes(cointerra, info, corehash_time);
+		cta_zero_corehashes(info);
+	}
+
+	mutex_lock(&info->lock);
+	hashes = info->share_hashes;
+	info->tot_share_hashes += info->share_hashes;
+	info->tot_calc_hashes += info->hashes;
+	info->hashes = info->share_hashes = 0;
+	mutex_unlock(&info->lock);
+
+	if (unlikely(cointerra->usbinfo.nodev))
+		hashes = -1;
+out:
+	return hashes;
+}
+
+/* This is used for a work restart. We don't actually perform the work restart
+ * here but wake up the scanwork loop if it's waiting on the conditional so
+ * that it can test for the restart message. */
+static void cta_wake(struct cgpu_info *cointerra)
+{
+	struct cointerra_info *info = cointerra->device_data;
+
+	mutex_lock(&info->lock);
+	pthread_cond_signal(&info->wake_cond);
+	mutex_unlock(&info->lock);
+}
+
+static void cta_shutdown(struct thr_info *thr)
+{
+	struct cgpu_info *cointerra = thr->cgpu;
+
+	cta_close(cointerra);
+}
+
+static void cta_zero_stats(struct cgpu_info *cointerra)
+{
+	struct cointerra_info *info = cointerra->device_data;
+
+	info->tot_calc_hashes = 0;
+	info->tot_reset_hashes = info->tot_hashes;
+	info->tot_share_hashes = 0;
+	cta_zero_corehashes(info);
+}
+
+static int bits_set(char v)
+{
+	int c;
+
+	for (c = 0; v; c++)
+		v &= v - 1;
+	return c;
+}
+
+static struct api_data *cta_api_stats(struct cgpu_info *cgpu)
+{
+	struct api_data *root = NULL;
+	struct cointerra_info *info = cgpu->device_data;
+	double dev_runtime = cgpu_runtime(cgpu);
+	int i, asic, core, coreno = 0;
+	struct timeval now;
+	char bitmaphex[36];
+	uint64_t ghs, val;
+	char buf[64];
+
+	/* Info data */
+	root = api_add_uint16(root, "HW Revision", &info->hwrev, false);
+	root = api_add_uint32(root, "Serial", &info->serial, false);
+	root = api_add_uint8(root, "Asics", &info->asics, false);
+	root = api_add_uint8(root, "Dies", &info->dies, false);
+	root = api_add_uint16(root, "Cores", &info->cores, false);
+	root = api_add_uint8(root, "Board number", &info->board_number, false);
+	sprintf(buf, "%u.%u.%u", info->fwrev[0], info->fwrev[1], info->fwrev[2]);
+	root = api_add_string(root, "FW Revision", buf, true);
+	sprintf(buf, "%04u-%02u-%02u", info->fw_year, info->fw_month, info->fw_day);
+	root = api_add_string(root, "FW Date", buf, true);
+	root = api_add_uint8(root, "Init diffbits", &info->init_diffbits, false);
+	root = api_add_uint8(root, "Min diffbits", &info->min_diffbits, false);
+	root = api_add_uint8(root, "Max diffbits", &info->max_diffbits, false);
+
+	/* Status readings */
+	for (i = 0; i < CTA_CORES; i++) {
+		sprintf(buf, "CoreTemp%d", i);
+		root = api_add_int16(root, buf, &info->coretemp[i], false);
+	}
+	root = api_add_int16(root, "Ambient Low", &info->ambtemp_low, false);
+	root = api_add_int16(root, "Ambient Avg", &info->ambtemp_avg, false);
+	root = api_add_int16(root, "Ambient High", &info->ambtemp_high, false);
+	for (i = 0; i < CTA_PUMPS; i++) {
+		sprintf(buf, "PumpRPM%d", i);
+		root = api_add_uint16(root, buf, &info->pump_tachs[i], false);
+	}
+	for (i = 0; i < CTA_FANS; i++) {
+		sprintf(buf, "FanRPM%d", i);
+		root = api_add_uint16(root, buf, &info->fan_tachs[i], false);
+	}
+	for (i = 0; i < CTA_CORES; i++) {
+		sprintf(buf, "CoreFreqs%d", i);
+		root = api_add_uint16(root, buf, &info->corefreqs[i], false);
+	}
+
+	for (i = 0; i < CTA_CORES; i++) {
+		sprintf(buf, "CoreVolts%d", i);
+		root = api_add_uint16(root, buf, &info->corevolts[i], false);
+	}
+	root = api_add_uint16(root, "Volts3.3", &info->volts33, false);
+	root = api_add_uint16(root, "Volts12", &info->volts12, false);
+	root = api_add_uint16(root, "Inactive", &info->inactive, false);
+	root = api_add_uint16(root, "Active", &info->active, false);
+
+	/* Status settings */
+	for (i = 0; i < CTA_CORES; i++) {
+		sprintf(buf, "CorePerfMode%d", i);
+		root = api_add_uint8(root, buf, &info->coreperf[i], false);
+	}
+	for (i = 0; i < CTA_FANS; i++) {
+		sprintf(buf, "FanSpeed%d", i);
+		root = api_add_uint8(root, buf, &info->fanspeed[i], false);
+	}
+	root = api_add_uint8(root, "DiesActive", &info->dies_active, false);
+	for (i = 0; i < CTA_CORES; i++) {
+		sprintf(buf, "PipesEnabled%d", i);
+		root = api_add_uint8(root, buf, &info->pipes_enabled[i], false);
+	}
+
+	/* Status debug */
+	root = api_add_int(root, "Underruns", &info->tot_underruns, false);
+	for (i = 0; i < CTA_CORES; i++) {
+		sprintf(buf, "HWErrors%d", i);
+		root = api_add_uint16(root, buf, &info->tot_hw_errors[i], false);
+	}
+	ghs = info->tot_calc_hashes / dev_runtime;
+	root = api_add_uint64(root, "Calc hashrate", &ghs, true);
+	ghs = (info->tot_hashes - info->tot_reset_hashes) / dev_runtime;
+	root = api_add_uint64(root, "Hashrate", &ghs, true);
+	ghs = info->tot_share_hashes / dev_runtime;
+	root = api_add_uint64(root, "Share hashrate", &ghs, true);
+	root = api_add_uint64(root, "Total calc hashes", &info->tot_calc_hashes, false);
+	ghs = info->tot_hashes - info->tot_reset_hashes;
+	root = api_add_uint64(root, "Total hashes", &ghs, true);
+	root = api_add_uint64(root, "Total raw hashes", &info->tot_hashes, false);
+	root = api_add_uint64(root, "Total share hashes", &info->tot_share_hashes, false);
+	root = api_add_uint64(root, "Total flushed hashes", &info->tot_flushed_hashes, false);
+	val = cgpu->diff_accepted * 0x100000000ull;
+	root = api_add_uint64(root, "Accepted hashes", &val, true);
+	ghs = val / dev_runtime;
+	root = api_add_uint64(root, "Accepted hashrate", &ghs, true);
+	val = cgpu->diff_rejected * 0x100000000ull;
+	root = api_add_uint64(root, "Rejected hashes", &val, true);
+	ghs = val / dev_runtime;
+	root = api_add_uint64(root, "Rejected hashrate", &ghs, true);
+
+	cgtime(&now);
+	dev_runtime = tdiff(&now, &info->core_hash_start);
+	if (dev_runtime < 1)
+		dev_runtime = 1;
+	for (i = 0; i < CTA_CORES; i++) {
+		sprintf(buf, "Core%d hashrate", i);
+		ghs = info->tot_core_hashes[i] / dev_runtime;
+		root = api_add_uint64(root, buf, &ghs, true);
+	}
+	root = api_add_uint32(root, "Uptime",&info->uptime,false);
+	for (asic = 0; asic < 2; asic++) {
+		for (core = 0; core < 4; core++) {
+			char bitmapcount[40], asiccore[12];
+			int count = 0;
+
+			sprintf(asiccore, "Asic%dCore%d", asic, core);
+			__bin2hex(bitmaphex, &info->pipe_bitmap[coreno], 16);
+			for (i = coreno; i < coreno + 16; i++)
+				count += bits_set(info->pipe_bitmap[i]);
+			snprintf(bitmapcount, 40, "%d:%s", count, bitmaphex);
+			root = api_add_string(root, asiccore, bitmapcount, true);
+			coreno += 16;
+		}
+	}
+	root = api_add_uint8(root, "AV", &info->autovoltage, false);
+	root = api_add_uint8(root, "Power Supply Percent", &info->current_ps_percent, false);
+	root = api_add_uint16(root, "Power Used", &info->power_used, false);
+	root = api_add_uint16(root, "IOUT", &info->power_used, false);
+	root = api_add_uint16(root, "VOUT", &info->power_voltage, false);
+	root = api_add_uint16(root, "IIN", &info->ipower_used, false);
+	root = api_add_uint16(root, "VIN", &info->ipower_voltage, false);
+	root = api_add_uint16(root, "PSTemp1", &info->power_temps[0], false);
+	root = api_add_uint16(root, "PSTemp2", &info->power_temps[1], false);
+
+	return root;
+}
+
+static void cta_statline_before(char *buf, size_t bufsiz, struct cgpu_info *cointerra)
+{
+	struct cointerra_info *info = cointerra->device_data;
+	double max_volt = 0;
+	int freq = 0, i;
+
+	for (i = 0; i < CTA_CORES; i++) {
+		if (info->corevolts[i] > max_volt)
+			max_volt = info->corevolts[i];
+		if (info->corefreqs[i] > freq)
+			freq = info->corefreqs[i];
+	}
+	max_volt /= 1000;
+
+	tailsprintf(buf, bufsiz, "%3dMHz %3.1fC %3.2fV", freq, cointerra->temp, max_volt);
+}
+
+struct device_drv cointerra_drv = {
+	.drv_id = DRIVER_cointerra,
+	.dname = "cointerra",
+	.name = "CTA",
+	.drv_detect = cta_detect,
+	.thread_prepare = cta_prepare,
+	.hash_work = hash_queued_work,
+	.queue_full = cta_fill,
+	.update_work = cta_update_work,
+	.scanwork = cta_scanwork,
+	.flush_work = cta_wake,
+	.get_api_stats = cta_api_stats,
+	.get_statline_before = cta_statline_before,
+	.thread_shutdown = cta_shutdown,
+	.zero_stats = cta_zero_stats,
+	.max_diff = 32, // Set it below the actual limit to check nonces
+};

+ 224 - 0
driver-cointerra.h

@@ -0,0 +1,224 @@
+/*
+ * Copyright 2013-2014 Con Kolivas <kernel@kolivas.org>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 3 of the License, or (at your option)
+ * any later version.  See COPYING for more details.
+ */
+
+#ifndef COINTERRA_H
+#define COINTERRA_H
+
+#define CTA_READBUF_SIZE 8192
+#define CTA_MSG_SIZE 64
+#define CTA_READ_TIMEOUT 1
+#define CTA_READ_INTERVAL 100
+#define CTA_SCAN_INTERVAL 500
+#define CTA_RESET_TIMEOUT 1000
+
+#define CTA_INIT_DIFF		32*0.9999847412109375
+
+#if 0
+/* FIXME: how big should this be? */
+#define CTA_MAX_QUEUE 2300
+#else
+#define CTA_MAX_QUEUE (32 / CTA_NROLL_TIME)
+#endif
+
+#define CTA_NROLL_TIME 2
+
+/* Offsets into buffer */
+#define CTA_MSG_TYPE		2
+#define CTA_RESET_TYPE		3
+#define CTA_RESET_DIFF		4
+#define CTA_RESET_LOAD		5
+#define CTA_RESET_PSLOAD	6
+#define CTA_DRIVER_TAG		3
+#define CTA_MCU_TAG		5
+#define CTA_MCU_CORE		5
+#define CTA_MCU_ASIC		6
+#define CTA_MCU_PIPE		8
+#define CTA_MATCH_NOFFSET	45
+#define CTA_MATCH_NONCE		60
+#define CTA_WDONE_NONCES	11
+#define CTA_MSG_RECVD		3
+#define CTA_WORK_MIDSTATE	9
+#define CTA_WORK_DATA		41
+#define CTA_WORK_NROLL		53
+#define CTA_WORK_DIFFBITS	55
+#define CTA_REQWORK_REQUESTS	3
+#define CTA_CORE_HASHRATES	3
+
+/* Received message types */
+#define CTA_RECV_UNUSED		0
+#define CTA_RECV_REQWORK	1
+#define CTA_RECV_MATCH		2
+#define CTA_RECV_WDONE		3
+#define CTA_RECV_STATREAD	4
+#define CTA_RECV_STATSET	5
+#define CTA_RECV_INFO		6
+#define CTA_RECV_MSG            7
+#define CTA_RECV_RDONE		8
+#define CTA_RECV_STATDEBUG	10
+
+/* Sent message types */
+#define CTA_SEND_UNUSED		0
+#define CTA_SEND_RESET		1
+#define CTA_SEND_WORK		2
+#define CTA_SEND_SETPERF	3
+#define CTA_SEND_REQUEST	4
+#define CTA_SEND_FMATCH		5
+#define CTA_SEND_IDENTIFY	6
+#define CTA_SEND_COREHASHRATE	7
+
+/* Types of reset in CTA_RESET_TYPE */
+#define CTA_RESET_NONE		0
+#define CTA_RESET_UPDATE	1
+#define CTA_RESET_NEW		2
+#define CTA_RESET_INIT		3
+
+#define CTA_INFO_HWREV		3
+#define CTA_INFO_SERNO		5
+#define CTA_INFO_NUMASICS	9
+#define CTA_INFO_NUMDIES	10
+#define CTA_INFO_NUMCORES	11
+#define CTA_INFO_BOARDNUMBER	13
+#define CTA_INFO_FWREV_MAJ	19
+#define CTA_INFO_FWREV_MIN	20
+#define CTA_INFO_FWREV_MIC	21
+#define CTA_INFO_FWDATE_YEAR	23
+#define CTA_INFO_FWDATE_MONTH	25
+#define CTA_INFO_FWDATE_DAY	26
+#define CTA_INFO_INITDIFFBITS	27
+#define CTA_INFO_MINDIFFBITS	28
+#define CTA_INFO_MAXDIFFBITS	29
+
+#define CTA_STAT_CORETEMPS	3
+#define CTA_STAT_AMBTEMP_LOW	19
+#define CTA_STAT_AMBTEMP_AVG	21
+#define CTA_STAT_AMBTEMP_HIGH	23
+#define CTA_STAT_PUMP_TACHS	25
+#define CTA_STAT_FAN_TACHS	29
+#define CTA_STAT_CORE_VOLTS	37
+#define CTA_STAT_VOLTS33	53
+#define CTA_STAT_VOLTS12	55
+#define CTA_STAT_INACTIVE	57
+#define CTA_STAT_ACTIVE		59
+
+#define CTA_STAT_PERFMODE	3
+#define CTA_STAT_FANSPEEDS	11
+#define CTA_STAT_DIES_ACTIVE	15
+#define CTA_STAT_PIPES_ENABLED	16
+#define CTA_STAT_MIN_FAN_SPEED 	24
+#define CTA_STAT_UPTIME		25
+#define CTA_STAT_HEARTBEATS	29
+#define CTA_STAT_CORE_FREQS	45
+
+#define CTA_STAT_UNDERRUNS	3
+#define CTA_STAT_HW_ERRORS	5
+#define CTA_STAT_UPTIME_MS	21
+#define CTA_STAT_HASHES		25
+#define CTA_STAT_FLUSHED_HASHES 33
+#define CTA_STAT_AUTOVOLTAGE	41
+#define CTA_STAT_POWER_PERCENT	42
+#define CTA_STAT_POWER_USED	43
+#define CTA_STAT_VOLTAGE	45
+#define CTA_STAT_IPOWER_USED	47
+#define CTA_STAT_IVOLTAGE	49
+#define CTA_STAT_PS_TEMP1	51
+#define CTA_STAT_PS_TEMP2	53
+
+
+#define CTA_CORES		8
+#define CTA_PUMPS		2
+#define CTA_FANS		4
+
+#define CTA_REQ_MSGTYPE		3
+#define CTA_REQ_INTERVAL	5
+
+
+int opt_cta_load;
+int opt_ps_load;
+
+struct cointerra_info {
+	/* Info data */
+	uint16_t hwrev;
+	uint32_t serial;
+	uint8_t asics;
+	uint8_t dies;
+	uint16_t cores;
+	uint8_t board_number;
+	uint8_t fwrev[3];
+	uint16_t fw_year;
+	uint8_t fw_month;
+	uint8_t fw_day;
+	uint8_t init_diffbits;
+	uint8_t min_diffbits;
+	uint8_t max_diffbits;
+
+	/* Status readings data */
+	uint16_t coretemp[CTA_CORES];
+	uint16_t ambtemp_low;
+	uint16_t ambtemp_avg;
+	uint16_t ambtemp_high;
+	uint16_t pump_tachs[CTA_PUMPS];
+	uint16_t fan_tachs[CTA_FANS];
+	uint16_t corevolts[CTA_CORES];
+	uint16_t volts33;
+	uint16_t volts12;
+	uint16_t inactive;
+	uint16_t active;
+	uint16_t corefreqs[CTA_CORES];
+	uint32_t uptime;
+
+	/* Status settings data */
+	uint8_t coreperf[CTA_CORES];
+	uint8_t fanspeed[CTA_FANS];
+	uint8_t dies_active;
+	uint8_t pipes_enabled[CTA_CORES];
+
+	/* Status debug data */
+	uint16_t underruns;
+	uint16_t hw_errors[CTA_CORES];
+
+	/* Running total from debug messages */
+	int tot_underruns;
+	uint16_t tot_hw_errors[CTA_CORES];
+	uint64_t tot_hashes;
+	uint64_t tot_reset_hashes;
+	uint64_t tot_flushed_hashes;
+	uint8_t  autovoltage;
+	uint8_t  current_ps_percent;
+	uint16_t power_used;
+	uint16_t power_voltage;
+	uint16_t ipower_used;
+	uint16_t ipower_voltage;
+	uint16_t power_temps[2];
+
+	bool autovoltage_complete;
+
+	/* Calculated totals based on work done and nonces found */
+	uint64_t hashes;
+	uint64_t tot_calc_hashes;
+
+	/* Calculated totals based on shares returned */
+	uint64_t share_hashes;
+	uint64_t tot_core_hashes[CTA_CORES];
+	uint64_t tot_share_hashes;
+	struct timeval core_hash_start;
+	int requested;
+	uint16_t work_id;
+	int no_matching_work;
+	time_t last_pipe_nonce[1024];
+	unsigned char pipe_bitmap[128];
+
+	struct thr_info *thr;
+	pthread_mutex_t lock;
+	pthread_mutex_t sendlock;
+	pthread_cond_t wake_cond;
+	pthread_t read_thr;
+	cgsem_t reset_sem;
+};
+
+#endif /* COINTERRA_H */