/ driver-spondoolies-sp10.c
driver-spondoolies-sp10.c
  1  /*
  2   * Copyright 2014 Con Kolivas <kernel@kolivas.org>
  3   * Copyright 2014 Zvi (Zvisha) Shteingart - Spondoolies-tech.com
  4   *
  5   * This program is free software; you can redistribute it and/or modify it
  6   * under the terms of the GNU General Public License as published by the Free
  7   * Software Foundation; either version 3 of the License, or (at your option)
  8   * any later version.  See COPYING for more details.
  9   */
 10  
 11  /*
 12   This driver communicates the job requests via Unix socket to the minergate
 13   process, that is responsible for controlling the Spondoolies Dawson SP10 miner.
 14  
 15   The jobs sent each with unique ID and returned asynchronously in one of the next
 16   transactions. REQUEST_PERIOD and REQUEST_SIZE define the communication rate with minergate.
 17  */
 18  
 19  #include <float.h>
 20  #include <limits.h>
 21  #include <pthread.h>
 22  #include <stdint.h>
 23  #include <stdio.h>
 24  #include <strings.h>
 25  #include <sys/time.h>
 26  #include <unistd.h>
 27  #include <assert.h>
 28  #include <time.h>
 29  #include <sys/socket.h>
 30  #include <sys/un.h>
 31  #include <unistd.h>
 32  #include <string.h>
 33  #include <math.h>
 34  
 35  #include "config.h"
 36  
 37  #include "compat.h"
 38  #include "miner.h"
 39  #include "driver-spondoolies-sp10-p.h"
 40  #include "driver-spondoolies-sp10.h"
 41  
 42  #ifdef WORDS_BIGENDIAN
 43  #  define swap32tobe(out, in, sz)  ((out == in) ? (void)0 : memmove(out, in, sz))
 44  #  define LOCAL_swap32be(type, var, sz)  ;
 45  #  define swap32tole(out, in, sz)  swap32yes(out, in, sz)
 46  #  define LOCAL_swap32le(type, var, sz)  LOCAL_swap32(type, var, sz)
 47  #else
 48  #  define swap32tobe(out, in, sz)  swap32yes(out, in, sz)
 49  #  define LOCAL_swap32be(type, var, sz)  LOCAL_swap32(type, var, sz)
 50  #  define swap32tole(out, in, sz)  ((out == in) ? (void)0 : memmove(out, in, sz))
 51  #  define LOCAL_swap32le(type, var, sz)  ;
 52  #endif
 53  
 54  static inline void swap32yes(void *out, const void *in, size_t sz)
 55  {
 56  	size_t swapcounter;
 57  
 58  	for (swapcounter = 0; swapcounter < sz; ++swapcounter)
 59  		(((uint32_t*)out)[swapcounter]) = swab32(((uint32_t*)in)[swapcounter]);
 60  }
 61  
 62  static void send_minergate_pkt(const minergate_req_packet* mp_req, minergate_rsp_packet* mp_rsp,
 63  			       int  socket_fd)
 64  {
 65  	int nbytes, nwrote, nread;
 66  
 67  	nbytes = sizeof(minergate_req_packet);
 68  	nwrote = write(socket_fd, (const void *)mp_req, nbytes);
 69  	if (unlikely(nwrote != nbytes))
 70  		_quit(-1);
 71  	nbytes = sizeof(minergate_rsp_packet);
 72  	nread = read(socket_fd, (void *)mp_rsp, nbytes);
 73  	if (unlikely(nread != nbytes))
 74  		_quit(-1);
 75  	passert(mp_rsp->magic == 0xcaf4);
 76  }
 77  
 78  static bool spondoolies_prepare(struct thr_info *thr)
 79  {
 80  	struct cgpu_info *spondoolies = thr->cgpu;
 81  	struct timeval now;
 82  
 83  	assert(spondoolies);
 84  	cgtime(&now);
 85  	/* FIXME: Vladik */
 86  #if NEED_FIX
 87  	get_datestamp(spondoolies->init, &now);
 88  #endif
 89  	return true;
 90  }
 91  
 92  static int init_socket(void)
 93  {
 94  	int socket_fd = socket(PF_UNIX, SOCK_STREAM, 0);
 95  	struct sockaddr_un address;
 96  
 97  	if (socket_fd < 0) {
 98  		printf("socket() failed\n");
 99  		perror("Err:");
100  		return 0;
101  	}
102  
103  	/* start with a clean address structure */
104  	memset(&address, 0, sizeof(struct sockaddr_un));
105  
106  	address.sun_family = AF_UNIX;
107  	sprintf(address.sun_path, MINERGATE_SOCKET_FILE);
108  
109  	if(connect(socket_fd, (struct sockaddr *) &address, sizeof(struct sockaddr_un))) {
110  		printf("connect() failed\n");
111  		perror("Err:");
112  		return 0;
113  	}
114  
115  	return socket_fd;
116  }
117  
118  static bool spondoolies_flush_queue(struct spond_adapter* a, bool flush_queue)
119  {
120  	if (!a->parse_resp) {
121  		static int i = 0;
122  
123  		if (i++ % 10 == 0 && a->works_in_minergate_and_pending_tx + a->works_pending_tx != a->works_in_driver)
124  			printf("%d + %d != %d\n", a->works_in_minergate_and_pending_tx, a->works_pending_tx,a->works_in_driver);
125  		assert(a->works_in_minergate_and_pending_tx + a->works_pending_tx == a->works_in_driver);
126  		send_minergate_pkt(a->mp_next_req,  a->mp_last_rsp, a->socket_fd);
127  		if (flush_queue)
128  			a->mp_next_req->mask |= 0x02;
129  		else
130  			a->mp_next_req->mask &= ~0x02;
131  
132  		a->mp_next_req->req_count = 0;
133  		a->parse_resp = 1;
134  		a->works_in_minergate_and_pending_tx += a->works_pending_tx;
135  		a->works_pending_tx = 0;
136  	}
137  	return true;
138  }
139  
140  static void spondoolies_detect(__maybe_unused bool hotplug)
141  {
142  	struct cgpu_info *cgpu = cgcalloc(1, sizeof(*cgpu));
143  	struct device_drv *drv = &sp10_drv;
144  	struct spond_adapter *a;
145  
146  #if NEED_FIX
147  	nDevs = 1;
148  #endif
149  
150  	assert(cgpu);
151  	cgpu->drv = drv;
152  	cgpu->deven = DEV_ENABLED;
153  	cgpu->threads = 1;
154  	cgpu->device_data = cgcalloc(sizeof(struct spond_adapter), 1);
155  	a = cgpu->device_data;
156  	a->cgpu = (void *)cgpu;
157  	a->adapter_state = ADAPTER_STATE_OPERATIONAL;
158  	a->mp_next_req = allocate_minergate_packet_req(0xca, 0xfe);
159  	a->mp_last_rsp = allocate_minergate_packet_rsp(0xca, 0xfe);
160  
161  	pthread_mutex_init(&a->lock, NULL);
162  	a->socket_fd = init_socket();
163  	if (a->socket_fd < 1) {
164  		printf("Error connecting to minergate server!");
165  		_quit(-1);
166  	}
167  
168  	assert(add_cgpu(cgpu));
169  	// Clean MG socket
170  	spondoolies_flush_queue(a, true);
171  	spondoolies_flush_queue(a, true);
172  	spondoolies_flush_queue(a, true);
173  	applog(LOG_DEBUG, "SPOND spondoolies_detect done");
174  }
175  
176  static struct api_data *spondoolies_api_stats(struct cgpu_info *cgpu)
177  {
178  	struct spond_adapter *a = cgpu->device_data;
179  	struct api_data *root = NULL;
180  
181  	root = api_add_int(root, "ASICs total rate", &a->temp_rate, false);
182  	root = api_add_int(root, "Temperature front", &a->front_temp, false);
183  	root = api_add_int(root, "Temperature rear top", &a->rear_temp_top, false);
184  	root = api_add_int(root, "Temperature rear bot", &a->rear_temp_bot, false);
185  
186  	return root;
187  }
188  
189  #if 0
190  static unsigned char get_leading_zeroes(const unsigned char *target)
191  {
192  	unsigned char leading = 0;
193  	int first_non_zero_chr;
194  	uint8_t m;
195  
196  	for (first_non_zero_chr = 31; first_non_zero_chr >= 0; first_non_zero_chr--) {
197  		if (target[first_non_zero_chr] == 0)
198  			leading += 8;
199  		else
200  			break;
201  	}
202  
203  	// j = first non-zero
204  	m = target[first_non_zero_chr];
205  	while ((m & 0x80) == 0) {
206  		leading++;
207  		m = m << 1;
208  	}
209  	return leading;
210  }
211  #endif
212  
213  static void spondoolies_shutdown(__maybe_unused struct thr_info *thr)
214  {
215  }
216  
217  static void fill_minergate_request(minergate_do_job_req* work, struct work *cg_work,
218  				   int ntime_offset)
219  {
220  	uint32_t x[64/4];
221  	uint64_t wd;
222  
223  	memset(work, 0, sizeof(minergate_do_job_req));
224  	//work->
225  	LOCAL_swap32le(unsigned char, cg_work->midstate, 32/4)
226  	LOCAL_swap32le(unsigned char, cg_work->data+64, 64/4)
227  	swap32yes(x, cg_work->data + 64, 64/4);
228  	memcpy(work->midstate, cg_work->midstate, 32);
229  	work->mrkle_root = ntohl(x[0]);
230  	work->timestamp  = ntohl(x[1]);
231  	work->difficulty = ntohl(x[2]);
232  	//work->leading_zeroes = get_leading_zeroes(cg_work->target);
233  	// Is there no better way to get leading zeroes?
234  	work->leading_zeroes = 30;
235  	wd = round(cg_work->work_difficulty);
236  	while (wd) {
237  		work->leading_zeroes++;
238  		wd = wd >> 1;
239  	}
240  	//printf("%d %d\n",work->leading_zeroes, (int)round(cg_work->work_difficulty));
241  	work->work_id_in_sw = cg_work->subid;
242  	work->ntime_limit = 0;
243  	work->ntime_offset = ntime_offset;
244  }
245  
246  // returns true if queue full.
247  static struct timeval last_force_queue = {0};
248  
249  static bool spondoolies_queue_full(struct cgpu_info *cgpu)
250  {
251  	// Only once every 1/10 second do work.
252  	struct spond_adapter* a = cgpu->device_data;
253  	int next_job_id, ntime_clones, i;
254  	struct timeval tv;
255  	struct work *work;
256  	unsigned int usec;
257  	bool ret = false;
258  
259  	mutex_lock(&a->lock);
260  	passert(a->works_pending_tx <= REQUEST_SIZE);
261  
262  	gettimeofday(&tv, NULL);
263  
264  	usec = (tv.tv_sec-last_force_queue.tv_sec) * 1000000;
265  	usec += (tv.tv_usec-last_force_queue.tv_usec);
266  
267  	if ((usec >= REQUEST_PERIOD) || (a->reset_mg_queue == 2) ||
268  	    ((a->reset_mg_queue == 1) && (a->works_pending_tx == REQUEST_SIZE))) {
269  		spondoolies_flush_queue(a, (a->reset_mg_queue == 2));
270  		if (a->reset_mg_queue)
271  			a->reset_mg_queue--;
272  		last_force_queue = tv;
273  	}
274  
275  	// see if we have enough jobs
276  	if (a->works_pending_tx == REQUEST_SIZE) {
277  		ret = true;
278  		goto return_unlock;
279  	}
280  
281  	// see if can take 1 more job.
282  	next_job_id = (a->current_job_id + 1) % MAX_JOBS_IN_MINERGATE;
283  	if (a->my_jobs[next_job_id].cgminer_work) {
284  		ret = true;
285  		goto return_unlock;
286  	}
287  	work = get_queued(cgpu);
288  	if (!work) {
289  		cgsleep_ms(10);
290  		goto return_unlock;
291  	}
292  
293  	work->thr = cgpu->thr[0];
294  	work->thr_id = cgpu->thr[0]->id;
295  	assert(work->thr);
296  
297  	// Create 5 works using ntime increment
298  	a->current_job_id = next_job_id;
299  	work->subid = a->current_job_id;
300  	// Get pointer for the request
301  	a->my_jobs[a->current_job_id].cgminer_work = work;
302  	a->my_jobs[a->current_job_id].state = SPONDWORK_STATE_IN_BUSY;
303  	a->my_jobs[a->current_job_id].ntime_clones = 0;
304  
305  	ntime_clones = (work->drv_rolllimit < MAX_NROLES) ? work->drv_rolllimit : MAX_NROLES;
306  	for (i = 0 ; (i < ntime_clones) && (a->works_pending_tx < REQUEST_SIZE) ; i++) {
307  		minergate_do_job_req* pkt_job =  &a->mp_next_req->req[a->works_pending_tx];
308  		fill_minergate_request(pkt_job, work, i);
309  		a->works_in_driver++;
310  		a->works_pending_tx++;
311  		a->mp_next_req->req_count++;
312  		a->my_jobs[a->current_job_id].merkle_root = pkt_job->mrkle_root;
313  		a->my_jobs[a->current_job_id].ntime_clones++;
314  	}
315  
316  return_unlock:
317  	mutex_unlock(&a->lock);
318  
319  	return ret;
320  }
321  
322  static void spond_poll_stats(struct cgpu_info *spond, struct spond_adapter *a)
323  {
324  	FILE *fp = fopen("/var/run/mg_rate_temp", "r");
325  
326  	if (!fp) {
327  		applog(LOG_DEBUG, "SPOND unable to open mg_rate_temp");
328  		a->temp_rate = a->front_temp = a->rear_temp_top = a->rear_temp_bot = 0;
329  	} else {
330  		int ret = fscanf(fp, "%d %d %d %d", &a->temp_rate,  &a->front_temp , &a->rear_temp_top , &a->rear_temp_bot);
331  
332  		if (ret != 4)
333  			a->temp_rate = a->front_temp = a->rear_temp_top = a->rear_temp_bot = 0;
334  		fclose(fp);
335  	}
336  	applog(LOG_DEBUG, "SPOND poll_stats rate: %d front: %d rear(T/B): %d/%d",
337  	       a->temp_rate, a->front_temp , a->rear_temp_top, a->rear_temp_bot);
338  	/* Use the rear temperature as the dev temperature for now */
339  	spond->temp = (a->rear_temp_top + a->rear_temp_bot)/2;
340  }
341  
342  // Return completed work to submit_nonce() and work_completed() 
343  // struct timeval last_force_queue = {0};  
344  static int64_t spond_scanhash(struct thr_info *thr)
345  {
346  	struct cgpu_info *cgpu = thr->cgpu;
347  	struct spond_adapter *a = cgpu->device_data;
348  	int64_t ghashes = 0;
349  	cgtimer_t cgt;
350  	time_t now_t;
351  
352  	cgsleep_prepare_r(&cgt);
353  	now_t = time(NULL);
354  	/* Poll stats only once per second */
355  	if (now_t != a->last_stats) {
356  		a->last_stats = now_t;
357  		spond_poll_stats(cgpu, a);
358  	}
359  
360  	if (a->parse_resp) {
361  		int array_size, i, j;
362  
363  		mutex_lock(&a->lock);
364  		ghashes = (a->mp_last_rsp->gh_div_10_rate);
365  		ghashes = ghashes  * 10000 * REQUEST_PERIOD;
366  		array_size = a->mp_last_rsp->rsp_count;
367  		for (i = 0; i < array_size; i++) { // walk the jobs
368  			int job_id;
369  
370  			minergate_do_job_rsp* work = a->mp_last_rsp->rsp + i;
371  			job_id = work->work_id_in_sw;
372  			if ((a->my_jobs[job_id].cgminer_work)) {
373  				if (a->my_jobs[job_id].merkle_root == work->mrkle_root) {
374  					assert(a->my_jobs[job_id].state == SPONDWORK_STATE_IN_BUSY);
375  					a->works_in_minergate_and_pending_tx--;
376  					a->works_in_driver--;
377  					for (j = 0; j < 2; j++) {
378  						if (work->winner_nonce[j]) {
379  							bool __maybe_unused ok;
380  							struct work *cg_work = a->my_jobs[job_id].cgminer_work;
381  #ifndef SP_NTIME
382  							ok = submit_nonce(cg_work->thr, cg_work, work->winner_nonce[j]);
383  #else
384  							ok = submit_noffset_nonce(cg_work->thr, cg_work, work->winner_nonce[j], work->ntime_offset);
385  #endif
386  							//printf("OK on %d:%d = %d\n",work->work_id_in_sw,j, ok);
387  							a->wins++;
388  						}
389  					}
390  					//printf("%d ntime_clones = %d\n",job_id,a->my_jobs[job_id].ntime_clones);
391  					if ((--a->my_jobs[job_id].ntime_clones) == 0) {
392  						//printf("Done with %d\n", job_id);
393  						work_completed(a->cgpu, a->my_jobs[job_id].cgminer_work);
394  						a->good++;
395  						a->my_jobs[job_id].cgminer_work = NULL;
396  						a->my_jobs[job_id].state = SPONDWORK_STATE_EMPTY;
397  					}
398  				} else {
399  					a->bad++;
400  					printf("Dropping minergate old job id=%d mrkl=%x my-mrkl=%x\n",
401  					       job_id, a->my_jobs[job_id].merkle_root, work->mrkle_root);
402  				}
403  			} else {
404  				a->empty++;
405  				printf("No cgminer job (id:%d res:%d)!\n",job_id, work->res);
406  			}
407  		}
408  		mutex_unlock(&a->lock);
409  
410  		a->parse_resp = 0;
411  	}
412  	cgsleep_ms_r(&cgt, 40);
413  
414  	return ghashes;
415  }
416  
417  // Remove all work from queue
418  static void spond_flush_work(struct cgpu_info *cgpu)
419  {
420  	struct spond_adapter *a = cgpu->device_data;
421  
422  	mutex_lock(&a->lock);
423  	a->reset_mg_queue = 2;
424  	mutex_unlock(&a->lock);
425  }
426  
427  struct device_drv sp10_drv = {
428  	.drv_id = DRIVER_sp10,
429  	.dname = "Spondoolies",
430  	.name = "SPN",
431  	.max_diff = 64.0, // Limit max diff to get some nonces back regardless
432  	.drv_detect = spondoolies_detect,
433  	.get_api_stats = spondoolies_api_stats,
434  	.thread_prepare = spondoolies_prepare,
435  	.thread_shutdown = spondoolies_shutdown,
436  	.hash_work = hash_queued_work,
437  	.queue_full = spondoolies_queue_full,
438  	.scanwork = spond_scanhash,
439  	.flush_work = spond_flush_work,
440  };