#include "config.h" #include #include #include #include #include #include #include #include #ifndef WIN32 #include #include #include #ifndef O_CLOEXEC #define O_CLOEXEC 0 #endif #else #ifdef WIN32 #include #endif #include #endif #include "elist.h" #include "miner.h" #include "driver-blockerupter.h" #include "usbutils.h" static void blockerupter_space_mode(struct cgpu_info *blockerupter) { int interface; unsigned int bits = 0; interface = usb_interface(blockerupter); bits |= CP210X_BITS_DATA_8; bits |= CP210X_BITS_PARITY_SPACE; usb_transfer_data(blockerupter, CP210X_TYPE_OUT, CP210X_SET_LINE_CTL, bits, interface, NULL, 0, C_SETPARITY); } static void blockerupter_mark_mode(struct cgpu_info *blockerupter) { int interface; unsigned int bits = 0; interface = usb_interface(blockerupter); bits |= CP210X_BITS_DATA_8; bits |= CP210X_BITS_PARITY_MARK; usb_transfer_data(blockerupter, CP210X_TYPE_OUT, CP210X_SET_LINE_CTL, bits, interface, NULL, 0, C_SETPARITY); } static void blockerupter_init_com(struct cgpu_info *blockerupter) { uint32_t baudrate; int interface; if (blockerupter->usbinfo.nodev) return; interface = usb_interface(blockerupter); // Enable the UART usb_transfer_data(blockerupter, CP210X_TYPE_OUT, CP210X_REQUEST_IFC_ENABLE, CP210X_VALUE_UART_ENABLE, interface, NULL, 0, C_ENABLE_UART); if (blockerupter->usbinfo.nodev) return; // Set data control usb_transfer_data(blockerupter, CP210X_TYPE_OUT, CP210X_REQUEST_DATA, CP210X_VALUE_DATA, interface, NULL, 0, C_SETDATA); if (blockerupter->usbinfo.nodev) return; // Set the baud baudrate = BET_BAUD; usb_transfer_data(blockerupter, CP210X_TYPE_OUT, CP210X_REQUEST_BAUD, 0, interface, &baudrate, sizeof (baudrate), C_SETBAUD); // Set space mode blockerupter_space_mode(blockerupter); } static int blockerupter_send(struct cgpu_info *blockerupter, char *data, int len) { int err; int bytes_sent; if (unlikely(blockerupter->usbinfo.nodev)) return SEND_FAIL; err = usb_write(blockerupter, data, len, &bytes_sent, C_BET_WRITE); if (err || bytes_sent != len) { applog(LOG_DEBUG, "blockerupter: Send (%d/%d)", bytes_sent, len); return SEND_FAIL; } return SEND_OK; } static int blockerupter_read(struct cgpu_info *blockerupter, char *data, int len) { int err; int bytes_read; if (unlikely(blockerupter->usbinfo.nodev)) return READ_FAIL; err = usb_read_timeout(blockerupter, data, len, &bytes_read, 2, C_BET_READ); if (err || bytes_read != len) { applog(LOG_DEBUG, "blockerupter: Read (%d/%d)", bytes_read, len); return READ_FAIL; } return READ_OK; } static void blockerupter_setclock(struct cgpu_info *blockerupter, uint8_t clock) { struct blockerupter_info *info; info = blockerupter->device_data; char command; int err; command = C_GCK | clock; info->clock = clock; err = blockerupter_send(blockerupter, &command, 1); if (!err) applog(LOG_DEBUG, "%s%d: Set Clock to %d MHz", blockerupter->drv->name, blockerupter->device_id, (clock + 1) * 10 / 2); } static void blockerupter_setdiff(struct cgpu_info *blockerupter, int diff) { struct blockerupter_info *info; info = blockerupter->device_data; char command,bits; int err; int local_diff; // min_diff for driver is 64 if (diff >= 262144) { bits = 3; local_diff = 262144; } else if (diff >= 4096) { bits = 2; local_diff = 4096; } else { bits = 1; local_diff = 64; } if (local_diff == info->diff) return; command = C_DIF | bits; err = blockerupter_send(blockerupter, &command, 1); if (!err) { applog(LOG_DEBUG, "%s%d: Set Diff Bits to %d", blockerupter->drv->name, blockerupter->device_id, bits); info->diff = local_diff; } } static void blockerupter_setrolling(struct cgpu_info *blockerupter, uint8_t rolling) { struct blockerupter_info *info; info = blockerupter->device_data; char command; int err; command = C_LPO | rolling; err = blockerupter_send(blockerupter, &command, 1); if (!err) { applog(LOG_DEBUG, "%s%d: Set nTime Rolling to %d seconds", blockerupter->drv->name, blockerupter->device_id, (rolling + 1) * 30); info->rolling = (rolling + 1) * 30; } } static void blockerupter_init(struct cgpu_info *blockerupter) { struct blockerupter_info *info; info = blockerupter->device_data; // Set Clock if (!opt_bet_clk || opt_bet_clk< 19 || opt_bet_clk > 31) { opt_bet_clk = BET_CLOCK_DEFAULT; } blockerupter_setclock(blockerupter, opt_bet_clk); info->clock = (opt_bet_clk + 1) * 10; info->expected = info->clock * 24 * 32 * info->found / 1000.0; // Set Diff blockerupter_setdiff(blockerupter, BET_DIFF_DEFAULT); info->diff = BET_DIFF_DEFAULT; // Set nTime Rolling blockerupter_setrolling(blockerupter, BET_ROLLING_DEFAULT); info->rolling = (BET_ROLLING_DEFAULT + 1) * 30; cgtime(&info->start_time); } static struct cgpu_info *blockerupter_detect_one(struct libusb_device *dev, struct usb_find_devices *found) { struct blockerupter_info *info; struct cgpu_info *blockerupter = usb_alloc_cgpu(&blockerupter_drv, 1); int i, err; char reset = C_RES; if (!usb_init(blockerupter, dev, found)) { applog(LOG_ERR, "Blockerupter usb init failed"); blockerupter = usb_free_cgpu(blockerupter); return NULL; } blockerupter->device_data = (struct blockerupter_info *) malloc(sizeof(struct blockerupter_info)); info = blockerupter->device_data; memset(info, 0, sizeof(blockerupter_info)); blockerupter_init_com(blockerupter); err = blockerupter_send(blockerupter, &reset, 1); if (err) { applog(LOG_ERR, "Blockerupter detect failed"); blockerupter = usb_free_cgpu(blockerupter); return NULL; } cgsleep_ms(5000); for (i = 0; i < BET_MAXBOARDS; i++) { char detect, answer; answer = 0; detect = C_ASK | (uint8_t)i; blockerupter_send(blockerupter, &detect, 1); blockerupter_read(blockerupter, &answer, 1); if (answer == A_WAL) { applog(LOG_DEBUG, "BlockErupter found Board: %d", i); info->boards[i] = 1; info->found++; } else { if (!i) { applog(LOG_DEBUG, "BlockErupter no boards found, likely not BET"); break; } applog(LOG_DEBUG, "BlockErupter missing board: %d, received %02x", i, answer); } } if (!info->found) { usb_uninit(blockerupter); blockerupter = usb_free_cgpu(blockerupter); free(info); return NULL; } else { blockerupter->threads = 1; add_cgpu(blockerupter); applog(LOG_DEBUG, "Add BlockErupter with %d/%d Boards", info->found, BET_MAXBOARDS); blockerupter_init(blockerupter); return blockerupter; } } static inline void blockerupter_detect(bool __maybe_unused hotplug) { usb_detect(&blockerupter_drv, blockerupter_detect_one); } static struct api_data *blockerupter_api_stats(struct cgpu_info *blockerupter) { struct blockerupter_info *info = blockerupter->device_data; struct api_data *root = NULL; struct timeval now, elapsed; char buf[32]; int i; cgtime(&now); timersub(&now, &info->start_time, &elapsed); info->hashrate = elapsed.tv_sec ? info->hashes * 4.295 / elapsed.tv_sec : 0; info->eff = info->hashrate / info->expected; root = api_add_int(root, "Nonces", &info->nonces, false); root = api_add_uint8(root, "Board", &info->found, false); root = api_add_int(root, "Clock", &info->clock, false); root = api_add_int(root,"Accepted", &info->accepted, false); root = api_add_double(root, "HashRate", &info->hashrate , false); root = api_add_double(root, "Expected", &info->expected , false); root = api_add_double(root, "Efficiency", &info->eff, false); for (i = 0; i < BET_MAXBOARDS; i++) { double brd_hashrate; if (info->boards[i]) { sprintf(buf, "Board%02d accepted", i); root = api_add_int(root, buf, &info->b_info[i].accepted, false); sprintf(buf, "Board%02d nonces", i); root = api_add_int(root, buf, &info->b_info[i].nonces, false); sprintf(buf, "Board%02d hwerror", i); root = api_add_double(root, buf, &info->b_info[i].hwe, false); sprintf(buf, "Board%02d hashrate", i); brd_hashrate = elapsed.tv_sec ? info->b_info[i].hashes * 4.295 / elapsed.tv_sec : 0; root = api_add_double(root, buf, &brd_hashrate, false); } } return root; } static bool blockerupter_prepare(struct thr_info *thr) { struct cgpu_info *blockerupter = thr->cgpu; struct blockerupter_info *info = blockerupter->device_data; cglock_init(&(info->pool.data_lock)); return true; } static void blockerupter_sendjob(struct cgpu_info *blockerupter, int board) { struct blockerupter_info *info = blockerupter->device_data; struct thr_info *thr = blockerupter->thr[0]; struct work *work; uint8_t command, answer; int err; work = get_work(thr, thr->id); memcpy(&info->works[info->work_idx],work,sizeof(struct work)); blockerupter_setdiff(blockerupter,floor(work->work_difficulty)); command = C_JOB | (uint8_t)board; blockerupter_send(blockerupter, (char *)&command, 1); blockerupter_mark_mode(blockerupter); cgsleep_ms(1); blockerupter_send(blockerupter, (char *)(work->midstate), 32); blockerupter_send(blockerupter, (char *)&(work->data[64]), 12); blockerupter_send(blockerupter, (char *)&work->nonce2, 4); blockerupter_send(blockerupter, (char *)&info->work_idx, 1); cgsleep_ms(1); blockerupter_space_mode(blockerupter); answer = 0; err = blockerupter_read(blockerupter, (char *)&answer, 1); cgtime(&info->last_job); if (err || answer != A_GET) { applog(LOG_ERR, "%s%d: Sync Error", blockerupter->drv->name, blockerupter->device_id); } else { info->b_info[board].job_count++; applog(LOG_DEBUG, "%s%d: Sent work %d to board %d", blockerupter->drv->name, blockerupter->device_id, info->work_idx, board); } info->work_idx++; if (info->work_idx >= BET_WORK_FIFO) info->work_idx = 0; } static uint64_t blockerupter_checknonce(struct cgpu_info *blockerupter, struct blockerupter_response *resp, int board) { uint8_t test; struct blockerupter_info *info; struct thr_info *thr = blockerupter->thr[0]; struct work work; uint32_t nonce; uint64_t hashes=0; int i; struct board_info *cur_brd; struct asic_info *cur_asic; info = blockerupter->device_data; work = info->works[resp->work_idx]; nonce = *(uint32_t *)resp->nonce; applog(LOG_DEBUG, "%s%d: Nonce %08x from board %d, asic %d for work %d", blockerupter->drv->name, blockerupter->device_id, *(uint32_t *) resp->nonce, board, resp->chip, resp->work_idx); memcpy(work.data + 4 + 32 + 32, resp->ntime, 4); __bin2hex(work.ntime, resp->ntime, 4); info->nonces++; cur_brd = &info->b_info[board]; cur_brd->nonces++; cur_asic = &info->b_info[board].asics[resp->chip]; cur_asic->nonces++; for (i = 0; i < BET_NONCE_FIX; i++) { test = test_nonce_diff(&work, nonce + i, (double)info->diff); if (test) { applog(LOG_DEBUG, "%s%d: Nonce Fix Pass @%d", blockerupter->drv->name, blockerupter->device_id, i); info->hashes += info->diff; cur_brd->hashes += info->diff; cur_asic->hashes += info->diff; if (test_nonce_diff(&work, nonce + i, work.work_difficulty)) { if (submit_nonce(thr, &work, nonce + i)) { hashes += floor(work.work_difficulty) * (uint64_t) 0xffffffff; info->accepted++; cur_brd->accepted++; cur_asic->accepted++; } } break; } } if (i == BET_NONCE_FIX) { applog(LOG_DEBUG, "%s%d: Nonce Fix Failed", blockerupter->drv->name, blockerupter->device_id); cur_brd->bad++; cur_brd->hwe = cur_brd->nonces ? (double)cur_brd->bad / cur_brd->nonces : 0; cur_asic->bad++; cur_asic->hwe = cur_asic->nonces ? (double)cur_asic->bad / cur_asic->nonces : 0; } return hashes; } static uint64_t blockerupter_getresp(struct cgpu_info *blockerupter, int board) { struct blockerupter_response *resp; int err; uint64_t hashes = 0; resp = (struct blockerupter_response *) malloc(BET_RESP_SZ); err = blockerupter_read(blockerupter, (char *)resp, BET_RESP_SZ); if (!err) hashes = blockerupter_checknonce(blockerupter, resp, board); free(resp); return hashes; } static int64_t blockerupter_scanhash(struct thr_info *thr) { struct cgpu_info *blockerupter = thr->cgpu; struct blockerupter_info *info = blockerupter->device_data; char ask; uint8_t answer; int i; int64_t hashes=0; if (unlikely(blockerupter->usbinfo.nodev)) { applog(LOG_ERR, "%s%d: Device disappeared, shutting down thread", blockerupter->drv->name, blockerupter->device_id); return -1; } for (i = 0; i < BET_MAXBOARDS; i++) { if (!info->boards[i]) continue; ask = C_ASK | (uint8_t)i; blockerupter_send(blockerupter, &ask, 1); cgsleep_ms(1); answer = 0; blockerupter_read(blockerupter, (char *)&answer, 1); switch (answer) { case A_WAL: blockerupter_sendjob(blockerupter, i); break; case A_YES: hashes += blockerupter_getresp(blockerupter, i); break; case A_NO: break; default: applog(LOG_ERR, "%s%d: Unexpected value %02x received", blockerupter->drv->name, blockerupter->device_id, answer); break; } } return hashes; } static void blockerupter_flush_work(struct cgpu_info *blockerupter) { uint8_t command = C_LPO | BET_ROLLING_DEFAULT; blockerupter_send(blockerupter, (char *)&command, 1); } struct device_drv blockerupter_drv = { .drv_id = DRIVER_blockerupter, .dname = "blockerupter", .name = "BET", .min_diff = 64, .get_api_stats = blockerupter_api_stats, .drv_detect = blockerupter_detect, .thread_prepare = blockerupter_prepare, .hash_work = hash_driver_work, .flush_work = blockerupter_flush_work, .scanwork = blockerupter_scanhash };