bool add_cgpu(struct cgpu_info *cgpu) { int lpcount; renumber_cgpu(cgpu); if (!cgpu->procs) cgpu->procs = 1; lpcount = cgpu->procs; cgpu->device = cgpu; cgpu->dev_repr = malloc(6); sprintf(cgpu->dev_repr, "%s%2u", cgpu->drv->name, cgpu->device_id % 100); cgpu->dev_repr_ns = malloc(6); sprintf(cgpu->dev_repr_ns, "%s%u", cgpu->drv->name, cgpu->device_id % 100); strcpy(cgpu->proc_repr, cgpu->dev_repr); sprintf(cgpu->proc_repr_ns, "%s%u", cgpu->drv->name, cgpu->device_id); #ifdef HAVE_FPGAUTILS maybe_strdup_if_null(&cgpu->dev_manufacturer, detectone_meta_info.manufacturer); maybe_strdup_if_null(&cgpu->dev_product, detectone_meta_info.product); maybe_strdup_if_null(&cgpu->dev_serial, detectone_meta_info.serial); #endif devices_new = realloc(devices_new, sizeof(struct cgpu_info *) * (total_devices_new + lpcount + 1)); devices_new[total_devices_new++] = cgpu; if (lpcount > 1) { int ns; int tpp = cgpu->threads / lpcount; struct cgpu_info **nlp_p, *slave; const bool manylp = (lpcount > 26); const char *as = (manylp ? "aa" : "a"); // Note, strcpy instead of assigning a byte to get the \0 too strcpy(&cgpu->proc_repr[5], as); ns = strlen(cgpu->proc_repr_ns); strcpy(&cgpu->proc_repr_ns[ns], as); nlp_p = &cgpu->next_proc; for (int i = 1; i < lpcount; ++i) { slave = malloc(sizeof(*slave)); *slave = *cgpu; slave->proc_id = i; if (manylp) { slave->proc_repr[5] += i / 26; slave->proc_repr[6] += i % 26; slave->proc_repr_ns[ns ] += i / 26; slave->proc_repr_ns[ns + 1] += i % 26; } else { slave->proc_repr[5] += i; slave->proc_repr_ns[ns] += i; } slave->threads = tpp; devices_new[total_devices_new++] = slave; *nlp_p = slave; nlp_p = &slave->next_proc; } *nlp_p = NULL; cgpu->proc_id = 0; cgpu->threads -= (tpp * (lpcount - 1)); } cgpu->last_device_valid_work = time(NULL); return true; }
bool cairnsmore_supports_dynclock(int fd) { if (!cairnsmore_send_cmd(fd, 0, 1, true)) return false; if (!cairnsmore_send_cmd(fd, 0, 1, true)) return false; uint32_t nonce = 0; { struct timeval tv_finish; struct thr_info dummy = { .work_restart = false, .work_restart_fd = -1, }; icarus_gets((unsigned char*)&nonce, fd, &tv_finish, &dummy, 1); } applog(LOG_DEBUG, "Cairnsmore dynclock detection... Got %08x", nonce); switch (nonce) { case 0x00949a6f: // big endian case 0x6f9a9400: // little endian // Hashed the command, so it's not supported return false; default: applog(LOG_WARNING, "Unexpected nonce from dynclock probe: %08x", be32toh(nonce)); return false; case 0: return true; } } #define cairnsmore_send_cmd(fd, cmd, data) cairnsmore_send_cmd(fd, cmd, data, false) static bool cairnsmore_change_clock_func(struct thr_info *thr, int bestM) { struct cgpu_info *cm1 = thr->cgpu; struct ICARUS_INFO *info = cm1->cgpu_data; if (unlikely(!cairnsmore_send_cmd(cm1->device_fd, 0, bestM))) return false; // Adjust Hs expectations for frequency change info->Hs = info->Hs * (double)bestM / (double)info->dclk.freqM; char repr[0x10]; sprintf(repr, "%s %u", cm1->api->name, cm1->device_id); dclk_msg_freqchange(repr, 2.5 * (double)info->dclk.freqM, 2.5 * (double)bestM, NULL); info->dclk.freqM = bestM; return true; } static bool cairnsmore_init(struct thr_info *thr) { struct cgpu_info *cm1 = thr->cgpu; struct ICARUS_INFO *info = cm1->cgpu_data; struct icarus_state *state = thr->cgpu_data; if (cairnsmore_supports_dynclock(cm1->device_fd)) { info->dclk_change_clock_func = cairnsmore_change_clock_func; dclk_prepare(&info->dclk); info->dclk.freqMinM = CAIRNSMORE1_MINIMUM_CLOCK / 2.5; info->dclk.freqMaxM = CAIRNSMORE1_MAXIMUM_CLOCK / 2.5; info->dclk.freqM = info->dclk.freqMDefault = CAIRNSMORE1_DEFAULT_CLOCK / 2.5; cairnsmore_send_cmd(cm1->device_fd, 0, info->dclk.freqM); applog(LOG_WARNING, "%s %u: Frequency set to %u MHz (range: %u-%u)", cm1->api->name, cm1->device_id, CAIRNSMORE1_DEFAULT_CLOCK, CAIRNSMORE1_MINIMUM_CLOCK, CAIRNSMORE1_MAXIMUM_CLOCK ); // The dynamic-clocking firmware connects each FPGA as its own device if (!(info->user_set & 1)) { info->work_division = 1; if (!(info->user_set & 2)) info->fpga_count = 1; } } else { applog(LOG_WARNING, "%s %u: Frequency scaling not supported", cm1->api->name, cm1->device_id ); } // Commands corrupt the hash state, so next scanhash is a firstrun state->firstrun = true; return true; } void convert_icarus_to_cairnsmore(struct cgpu_info *cm1) { struct ICARUS_INFO *info = cm1->cgpu_data; info->Hs = CAIRNSMORE1_HASH_TIME; info->fullnonce = info->Hs * (((double)0xffffffff) + 1); info->timing_mode = MODE_LONG; info->do_icarus_timing = true; cm1->api = &cairnsmore_api; renumber_cgpu(cm1); cairnsmore_init(cm1->thr[0]); } static struct api_data *cairnsmore_api_extra_device_status(struct cgpu_info *cm1) { struct ICARUS_INFO *info = cm1->cgpu_data; struct api_data*root = NULL; if (info->dclk.freqM) { double frequency = 2.5 * info->dclk.freqM; root = api_add_freq(root, "Frequency", &frequency, true); } return root; } static bool cairnsmore_identify(struct cgpu_info *cm1) { struct ICARUS_INFO *info = cm1->cgpu_data; if (!info->dclk.freqM) return false; cairnsmore_send_cmd(cm1->device_fd, 1, 1); sleep(5); cairnsmore_send_cmd(cm1->device_fd, 1, 0); cm1->flash_led = true; return true; } extern struct device_api icarus_api; static void cairnsmore_api_init() { cairnsmore_api = icarus_api; cairnsmore_api.dname = "cairnsmore"; cairnsmore_api.name = "ECM"; cairnsmore_api.api_detect = cairnsmore_detect; cairnsmore_api.thread_init = cairnsmore_init; cairnsmore_api.identify_device = cairnsmore_identify; cairnsmore_api.get_api_extra_device_status = cairnsmore_api_extra_device_status; }