/* * Copyright 2013-2014 Con Kolivas * * 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" 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_WARNING, "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]; uint8_t asic, core, coreno; asic = u8_from_msg(buf, CTA_MCU_ASIC); core = u8_from_msg(buf, CTA_MCU_CORE); coreno = asic * 4 + core; inc_hw_errors(thr); applog(LOG_WARNING, "%s %d: Notify bad match work", cointerra->drv->name, cointerra->device_id); if (coreno < CTA_CORES) info->fmatch_errors[coreno]++; 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 + 3, CTA_MSG_SIZE - 3); cta_send_msg(cointerra, sendbuf); } free_work(work); } else { applog(LOG_WARNING, "%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); applog(LOG_DEBUG, "%s %d: Done work found id 0x%X %d", cointerra->drv->name, cointerra->device_id, retwork, __LINE__); } else { applog(LOG_WARNING, "%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_irstat(struct cointerra_info *info, char *buf) { uint8_t channel = u8_from_msg(buf,CTA_IRSTAT_CHANNEL); if (channel >= CTA_CORES) return; mutex_lock(&info->lock); info->irstat_vin[channel] = hu16_from_msg(buf,CTA_IRSTAT_VIN); info->irstat_iin[channel] = hu16_from_msg(buf,CTA_IRSTAT_IIN); info->irstat_vout[channel] = hu16_from_msg(buf,CTA_IRSTAT_VOUT); info->irstat_iout[channel] = hu16_from_msg(buf,CTA_IRSTAT_IOUT); info->irstat_temp1[channel] = hu16_from_msg(buf,CTA_IRSTAT_TEMP1); info->irstat_temp2[channel] = hu16_from_msg(buf,CTA_IRSTAT_TEMP2); info->irstat_pout[channel] = hu16_from_msg(buf,CTA_IRSTAT_POUT); info->irstat_pin[channel] = hu16_from_msg(buf,CTA_IRSTAT_PIN); info->irstat_efficiency[channel] = hu16_from_msg(buf,CTA_IRSTAT_EFF); info->irstat_status[channel] = hu16_from_msg(buf,CTA_IRSTAT_STATUS); 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); 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 int verify_checksum(char *buf) { unsigned char checksum = 0; unsigned char i; for (i = 0; i < 63; i++) checksum += buf[i]; return (checksum == buf[63]); } static void cta_parse_msg(struct thr_info *thr, struct cgpu_info *cointerra, struct cointerra_info *info, char *buf) { if ((buf[CTA_MSG_TYPE] != CTA_RECV_MATCH)&& (buf[CTA_MSG_TYPE] != CTA_RECV_WDONE)) { if (unlikely(verify_checksum(buf) == 0)) { inc_hw_errors(thr); applog(LOG_WARNING, "%s %d: checksum bad",cointerra->drv->name,cointerra->device_id); return; } } 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; case CTA_RECV_IRSTAT: cta_parse_irstat(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 < 5) { 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]; #if 0 k[i] /= ((double)32 * (double)0x100000000ull); k[i] = sqrt(k[i]) + 1; k[i] *= k[i]; k[i] = k[i] * 32 * ((double)0x100000000ull ); #endif k[i] /= ((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; uint32_t runtime; 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; runtime = cgpu_runtime(thr->cgpu); runtime /= 30; info->old_hashes[runtime % 32] = info->tot_calc_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; int i; info->tot_calc_hashes = 0; info->tot_reset_hashes = info->tot_hashes; info->tot_share_hashes = 0; cta_zero_corehashes(info); for (i = 0; i < 16 * 2; i++) info->old_hashes[i] = 0; } 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]; uint32_t runtime = cgpu_runtime(cgpu); /* 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); //root = api_add_uint64(root, "cgminer 15m Hashrate", &cgpu->rolling15, true); // get runtime in 30 second steps runtime = runtime / 30; // store the current hashes info->old_hashes[runtime%32] = info->tot_calc_hashes; // calc the 15 minute average hashrate ghs = (info->old_hashes[(runtime+31)%32] - info->old_hashes[(runtime+1)%32])/(15*60); root = api_add_uint64(root, "15m 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); //if (info->power_used != 0) { { double value = info->power_used/100.0; value *= (info->power_voltage/100.0); root = api_add_double(root, "Power Used", &value, true); } 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); //} for (core = 0; core < CTA_CORES; core++) { char name[20]; char str[20]; double value; sprintf(name,"IRVIN%d",core+1); value = info->irstat_vin[core]/100.0; root = api_add_double(root,name,&value,true); sprintf(name,"IRIIN%d",core+1); value = info->irstat_iin[core]/100.0; root = api_add_double(root,name,&value,true); sprintf(name,"IRVOUT%d",core+1); value = info->irstat_vout[core]/100.0; root = api_add_double(root,name,&value,true); sprintf(name,"IRIOUT%d",core+1); value = info->irstat_iout[core]/100.0; root = api_add_double(root,name,&value,true); sprintf(name,"IRTEMP1_%d",core+1); value = info->irstat_temp1[core]/100.0; root = api_add_double(root,name,&value,true); sprintf(name,"IRTEMP2_%d",core+1); value = info->irstat_temp2[core]/100.0; root = api_add_double(root,name,&value,true); sprintf(name,"IRPOUT%d",core+1); value = info->irstat_pout[core]/100.0; root = api_add_double(root,name,&value,true); sprintf(name,"IRPIN%d",core+1); value = info->irstat_pin[core]/100.0; root = api_add_double(root,name,&value,true); sprintf(name,"IREFFICIENCY%d",core+1); value = info->irstat_efficiency[core]/100.0; root = api_add_double(root,name,&value,true); sprintf(name,"IRSTATUS%d",core+1); //root = api_add_uint16(root,name,&info->irstat_status[core],false); sprintf(str,"0x%04X",info->irstat_status[core]); root = api_add_string(root, name, str, true); } for (i = 0; i < CTA_CORES; i++) { sprintf(buf, "CoreFmatch%d", i); root = api_add_int16(root, buf, &info->fmatch_errors[i], 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 = 64, // Set it below the actual limit to check nonces };