/ 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 };