Example #1
0
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;
}
Example #2
0
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;
}