/* * Copyright 2013 Faster * Copyright 2012-2013 Andrew Smith * Copyright 2012 Luke Dashjr * Copyright 2012 Con Kolivas * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 3 of the License, or (at your option) * any later version. See COPYING for more details. */ #include "config.h" #include #include #include #include #include #include #include #include #ifndef WIN32 #include #include #include #include #else #include "compat.h" #include #include #include #endif /* WIN32 */ #include "miner.h" #include "usbutils.h" #include "fpgautils.h" #include "elist.h" #include "util.h" #include "driver-gridseed.h" #define using_libusb(info) ((info)->using_libusb > 0) #define using_serial(info) ((info)->using_libusb == 0) static const char *gridseed_version = "v3.8.5.20140210.02"; static const char *str_reset[] = { "55AAC000808080800000000001000000", // Chip reset "55AAC000E0E0E0E00000000001000000", // FW reset NULL }; static const char *str_init[] = { "55AAC000C0C0C0C00500000001000000", "55AAEF020000000000000000000000000000000000000000", "55AAEF3020000000", NULL }; static const char *str_ltc_reset[] = { "55AA1F2817000000", "55AA1F2814000000", "55AA1F2817000000", NULL }; static const char *str_nofifo[] = { "55AAC000D0D0D0D00000000001000000", NULL }; #ifdef WIN32 static void __maybe_unused set_text_color(WORD color) { SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), color); } #endif #ifdef WIN32 static char *win32strerror(DWORD err) { static char errstr[256]; FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_MAX_WIDTH_MASK, NULL, err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&errstr, sizeof(errstr), NULL); return errstr; } #endif #ifdef WIN32 #define sockclose(sock) closesocket(sock) #define sockerror(err) ((err) == SOCKET_ERROR) #define sockerrorstr() win32strerror(WSAGetLastError()) #else #define sockclose(sock) close(sock) #define sockerror(err) ((err) < 0) #define sockerrorstr() strerror(errno) #endif // Unset upon first hotplug check static bool initial_startup_phase = true; /*---------------------------------------------------------------------------------------*/ static int gridseed_send_ping_packet(GRIDSEED_INFO *, struct sockaddr_in); static void *gridseed_recv_packet(void *); static void gc3355_send_cmds(struct cgpu_info *, const char **); static bool gridseed_send_work_usb(struct cgpu_info *, unsigned char *, unsigned char *, unsigned char *, int, enum gsd_mode); static void __gridseed_test_ltc_nonce(struct cgpu_info *, GRIDSEED_INFO *, struct thr_info *, uint32_t, unsigned int); static int check_udp_port_in_use(short port) { int sock; struct sockaddr_in local; sock = socket(AF_INET, SOCK_DGRAM, 0); #ifndef WIN32 if (sock < 0) return -1; #else if ((unsigned int)sock == INVALID_SOCKET) return -1; #endif local.sin_family = AF_INET; local.sin_port = htons(port); local.sin_addr.s_addr = inet_addr("127.0.0.1"); if (sockerror(bind(sock, (struct sockaddr*)&local, sizeof(local)))) { sockclose(sock); return -1; } return sock; } static void gridseed_create_proxy(struct cgpu_info *gridseed, GRIDSEED_INFO *info) { int sock = info->sockltc; short port = info->ltc_port; if (sock != -1) sockclose(sock); info->sockltc = -1; while (true) { sock = check_udp_port_in_use(port); if (sock > -1) break; port++; } info->sockltc = sock; info->ltc_port = port; applog(LOG_NOTICE, "Create scrypt proxy on %d/UDP for %s%d", info->ltc_port, gridseed->drv->name, gridseed->device_id); } static bool gridseed_find_proxy(GRIDSEED_INFO *info) { struct sockaddr_in remote; struct timeval tv_timeout; GRIDSEED_PACKET packet; fd_set rdfs; int addrlen, n, sock = info->sockltc; short port = info->ltc_port + 1000; if (sock != -1) sockclose(sock); info->sockltc = -1; while (true) { sock = check_udp_port_in_use(port); if (sock > -1) break; port++; } info->ltc_port = port - 1000; info->sockltc = sock; remote.sin_family = AF_INET; remote.sin_port = htons(info->ltc_port); remote.sin_addr.s_addr = inet_addr("127.0.0.1"); applog(LOG_INFO, "Checking for scrypt proxy on %d/UDP", info->ltc_port); if (gridseed_send_ping_packet(info, remote) != 0) { sockclose(sock); return false; } tv_timeout.tv_sec = 0; tv_timeout.tv_usec = 500000; FD_ZERO(&rdfs); FD_SET(sock, &rdfs); if (select(sock+1, &rdfs, NULL, NULL, &tv_timeout) != 1) { sockclose(sock); return false; } addrlen = sizeof(remote); n = recvfrom(sock, (char*)&packet, sizeof(packet), 0, (struct sockaddr *)&remote, (socklen_t *)&addrlen); if (sockerror(n) || n != sizeof(packet)) { sockclose(sock); return false; } if (gridseed_send_ping_packet(info, remote) != 0) { sockclose(sock); return false; } //if (cgsem_mswait(&info->psem, 500) != 0) // return false; applog(LOG_NOTICE, "Found scrypt proxy on %d/UDP", info->ltc_port); return true; } static int gridseed_send_ping_packet(GRIDSEED_INFO *info, struct sockaddr_in to) { GRIDSEED_PACKET packet; if (info->sockltc < 0) return -1; packet.type = PACKET_PING; if (sendto(info->sockltc, (char*)&packet, sizeof(packet), 0, (struct sockaddr *)&to, sizeof(to)) != sizeof(packet)) { applog(LOG_WARNING, "Couldn't send ping packet: %s", sockerrorstr()); return -1; } return 0; } static int gridseed_send_info_packet(GRIDSEED_INFO *info, struct sockaddr_in to) { GRIDSEED_PACKET packet; if (info->sockltc < 0) return -1; packet.type = PACKET_INFO; packet.info.freq = info->freq; packet.info.chips = info->chips; packet.info.modules = info->modules; strncpy(packet.info.id, info->id, sizeof(packet.info.id)); if (sendto(info->sockltc, (char*)&packet, sizeof(packet), 0, (struct sockaddr *)&to, sizeof(to)) != sizeof(packet)) { applog(LOG_WARNING, "Couldn't send info packet: %s", sockerrorstr()); return -1; } return 0; } static void gridseed_recv_info_packet(struct cgpu_info *gridseed, GRIDSEED_INFO *info, GRIDSEED_PACKET packet, struct sockaddr_in from) { mutex_lock(&info->qlock); info->freq = packet.info.freq; info->chips = packet.info.chips; info->modules = packet.info.modules; strncpy(info->id, packet.info.id, sizeof(info->id)); gridseed->unique_id = info->id; info->toaddr = from; mutex_unlock(&info->qlock); cgsem_post(&info->psem); } static bool gridseed_send_work_packet(GRIDSEED_INFO *info, struct work *work) { GRIDSEED_PACKET packet; if (info->sockltc < 0) return false; packet.type = PACKET_WORK; memcpy(packet.work.target, work->target, sizeof(packet.work.target)); memcpy(packet.work.midstate, work->midstate, sizeof(packet.work.midstate)); memcpy(packet.work.data, work->data, sizeof(packet.work.data)); packet.work.id = work->id; if (sendto(info->sockltc, (char*)&packet, sizeof(packet), 0, (struct sockaddr *)&(info->toaddr), sizeof(info->toaddr)) != sizeof(packet)) { applog(LOG_WARNING, "Couldn't send work packet: %s", sockerrorstr()); return false; } return true; } static void gridseed_recv_work_packet(struct cgpu_info *gridseed, GRIDSEED_INFO *info, GRIDSEED_PACKET packet, struct sockaddr_in from) { mutex_lock(&info->qlock); info->toaddr = from; gridseed_send_work_usb(gridseed, packet.work.target, packet.work.midstate, packet.work.data, packet.work.id, MODE_SCRYPT_DUAL); mutex_unlock(&info->qlock); } static int gridseed_send_nonce_packet(GRIDSEED_INFO *info, unsigned char *data) { GRIDSEED_PACKET packet; uint32_t nonce; int workid; if (info->sockltc < 0) return -1; memcpy(&workid, data+8, 4); memcpy(&nonce, data+4, 4); packet.type = PACKET_NONCE; packet.nonce.nonce = nonce; packet.nonce.workid = workid; if (sendto(info->sockltc, (char*)&packet, sizeof(packet), 0, (struct sockaddr *)&(info->toaddr), sizeof(info->toaddr)) != sizeof(packet)) { applog(LOG_WARNING, "Couldn't send nonce packet: %s", sockerrorstr()); return -1; } return 0; } static void gridseed_recv_nonce_packet(struct cgpu_info *gridseed, GRIDSEED_INFO *info, GRIDSEED_PACKET packet) { __gridseed_test_ltc_nonce(gridseed, info, info->thr, packet.nonce.nonce, packet.nonce.workid); } static void *gridseed_recv_packet(void *userdata) { struct cgpu_info *gridseed = (struct cgpu_info *)userdata; GRIDSEED_INFO *info = gridseed->device_data; GRIDSEED_PACKET packet; struct timeval ts_packet, ts_now, tv_timeout; struct sockaddr_in fromaddr; fd_set rdfs; char threadname[24]; int addrlen, n, no_packets = 0, sock = info->sockltc; snprintf(threadname, sizeof(threadname), "GridSeed_Packet/%d", gridseed->device_id); RenameThread(threadname); applog(LOG_INFO, "GridSeed: packet thread running, %s", threadname); cgtime(&ts_packet); // initialize while(likely(!gridseed->shutdown)) { cgtime(&ts_now); if (info->mode == MODE_SCRYPT_DUAL && tdiff(&ts_now, &ts_packet) > (120 + 60 * no_packets)) { if (no_packets > 5) { applog(LOG_ERR, "%s%d: Proxy not responding, shutting down", gridseed->drv->name, gridseed->device_id); gridseed->shutdown = true; break; } else { applog(LOG_NOTICE, "%s%d: No data from proxy, sending ping", gridseed->drv->name, gridseed->device_id); gridseed_send_ping_packet(info, info->toaddr); } no_packets++; } else if (info->mode == MODE_SHA256_DUAL && tdiff(&ts_now, &ts_packet) > 130) { info->mode = MODE_SHA256; mutex_lock(&info->qlock); gc3355_send_cmds(gridseed, str_ltc_reset); cgsleep_ms(50); mutex_unlock(&info->qlock); } tv_timeout.tv_sec = 2; tv_timeout.tv_usec = 0; FD_ZERO(&rdfs); FD_SET(sock, &rdfs); n = select(sock+1, &rdfs, NULL, NULL, &tv_timeout); if (n == 0) continue; if (n < 0) { if (errno == EINTR) continue; applog(LOG_ERR, "Error calling select: %s", sockerrorstr()); gridseed->shutdown = true; break; } addrlen = sizeof(fromaddr); n = recvfrom(sock, (char*)&packet, sizeof(packet), 0, (struct sockaddr *)&fromaddr, (socklen_t *)&addrlen); if (sockerror(n)) { if (errno == EINTR) continue; applog(LOG_ERR, "Error calling recvfrom: %s", sockerrorstr()); gridseed->shutdown = true; break; } if (n != sizeof(packet)) continue; cgtime(&ts_packet); switch (packet.type) { case PACKET_PING: if (!SHA256_MODE(info->mode)) break; gridseed_send_info_packet(info, fromaddr); applog(LOG_INFO, "Received ping packet"); break; case PACKET_INFO: if (!SCRYPT_MODE(info->mode)) break; gridseed_recv_info_packet(gridseed, info, packet, fromaddr); applog(LOG_INFO, "Received info packet"); break; case PACKET_WORK: if (!SHA256_MODE(info->mode)) break; info->mode = MODE_SHA256_DUAL; gridseed_recv_work_packet(gridseed, info, packet, fromaddr); applog(LOG_INFO, "Received work packet"); break; case PACKET_NONCE: if (!SCRYPT_MODE(info->mode)) break; gridseed_recv_nonce_packet(gridseed, info, packet); applog(LOG_INFO, "Received nonce packet"); break; default: applog(LOG_ERR, "Received unknown packet"); break; } } return NULL; } /*---------------------------------------------------------------------------------------*/ /************************************************************ * Utility Functions ************************************************************/ static void flush_uart(int fd) { #ifdef WIN32 const HANDLE fh = (HANDLE)_get_osfhandle(fd); PurgeComm(fh, PURGE_RXCLEAR); #else tcflush(fd, TCIFLUSH); #endif } static void _transfer(struct cgpu_info *gridseed, uint8_t request_type, uint8_t bRequest, uint16_t wValue, uint16_t wIndex, uint32_t *data, int siz, enum usb_cmds cmd) { int err; err = usb_transfer_data(gridseed, request_type, bRequest, wValue, wIndex, data, siz, cmd); cgsleep_ms(GRIDSEED_COMMAND_DELAY); applog(LOG_DEBUG, "%s: cgid %d %s got err %d", gridseed->drv->name, gridseed->cgminer_id, usb_cmdname(cmd), err); } /************************************************************ * I/O helper functions ************************************************************/ #define gridseed_serial_open_detect(devpath, baud, purge) serial_open_ex(devpath, baud, 2, 0, purge, true) #define gridseed_serial_open(devpath, baud, purge) serial_open_ex(devpath, baud, 2, 0, purge, true) #define gridseed_serial_close(fd) close(fd) static bool gridseed_reopen(struct cgpu_info *gridseed) { GRIDSEED_INFO *info = gridseed->device_data; int try, fd = -1; if (!using_serial(info)) // sanity check return false; if (info->device_fd != -1) { applog(LOG_DEBUG, "Closing %s%d on %s (fd=%d)", gridseed->drv->name, gridseed->device_id, gridseed->device_path, info->device_fd); gridseed_serial_close(info->device_fd); info->device_fd = -1; cgsleep_ms(2000); } applog(LOG_DEBUG, "Attempting to open %s%d on %s", gridseed->drv->name, gridseed->device_id, gridseed->device_path); for (try = 0; try < 3; ++try) { fd = gridseed_serial_open(gridseed->device_path, info->baud, true); if (likely(fd > -1)) break; cgsleep_ms(3000); } if (unlikely(fd < 0)) { applog(LOG_ERR, "Failed to open %s%d on %s (%d attempts)", gridseed->drv->name, gridseed->device_id, gridseed->device_path, try); return false; } info->device_fd = fd; applog(LOG_DEBUG, "Successfully opened %s%d on %s (%d attempts, fd=%d)", gridseed->drv->name, gridseed->device_id, gridseed->device_path, try, info->device_fd); return true; } static int gc3355_write(struct cgpu_info *gridseed, const void *buf, size_t len) { GRIDSEED_INFO *info = gridseed->device_data; int ret, err; if (opt_debug) { char *hexstr; hexstr = bin2hex(buf, len); applog(LOG_DEBUG, "> %s", hexstr); free(hexstr); } #if 0 if (!opt_quiet && opt_debug) { int i; #ifndef WIN32 fprintf(stderr, " >>> %d : ", size); #else set_text_color(FOREGROUND_RED|FOREGROUND_GREEN); fprintf(stderr, " >>> %d : ", size); set_text_color(FOREGROUND_RED|FOREGROUND_GREEN|FOREGROUND_BLUE); #endif for(i=0; idrv->name, gridseed->device_id, libusb_strerror(err)); return -1; } else if (ret < 0 || (size_t)ret != len) { applog(LOG_ERR, "%s%d: usb_write length mismatch: %zu != %d", gridseed->drv->name, gridseed->device_id, len, ret); return -1; } } else { #ifndef WIN32 ret = write(info->device_fd, buf, len); #else ret = win32write(info->device_fd, buf, len); #endif if (ret < 0) { applog(LOG_ERR, "%s%d: error on serial write (fd=%d): %s", gridseed->drv->name, gridseed->device_id, info->device_fd, strerror(errno)); info->serial_reopen = true; return -1; } } cgsleep_ms(GRIDSEED_COMMAND_DELAY); return ret; } #define gc3355_get_data(gridseed, buf, len) gc3355_read(gridseed, buf, len, 1) static int gc3355_read(struct cgpu_info *gridseed, void *buf, size_t len, int read_count) { GRIDSEED_INFO *info = gridseed->device_data; ssize_t ret; size_t total = 0; int err, rc = 0; while (total < len) { if (using_libusb(info)) { err = usb_read_once_timeout(gridseed, (char *)buf + total, len - total, (int*)&ret, 200, C_GETRESULTS); if (err != LIBUSB_SUCCESS && err != LIBUSB_ERROR_TIMEOUT) { applog(LOG_ERR, "%s%d: error on USB read: %s", gridseed->drv->name, gridseed->device_id, libusb_strerror(err)); return -1; } } else { #ifndef WIN32 ret = read(info->device_fd, buf + total, len - total); #else ret = win32read(info->device_fd, buf + total, len - total); #endif if (ret < 0) { applog(LOG_ERR, "%s%d: error on serial read (fd=%d): %s", gridseed->drv->name, gridseed->device_id, info->device_fd, strerror(errno)); info->serial_reopen = true; return -1; } } if (ret == 0 && ++rc >= read_count) break; total += (size_t)ret; } if (opt_debug) { char *hexstr; if (total > 0) { hexstr = bin2hex(buf, total); applog(LOG_DEBUG, "< %s", hexstr); free(hexstr); } else { applog(LOG_DEBUG, "< (no data)"); } } #if 0 if (!opt_quiet && opt_debug) { int i; #ifndef WIN32 fprintf(stderr, " <<< %d : ", offset); #else set_text_color(FOREGROUND_RED); fprintf(stderr, " <<< %d : ", offset); set_text_color(FOREGROUND_RED|FOREGROUND_GREEN|FOREGROUND_BLUE); #endif for(i = 0; i < offset; i++) { fprintf(stderr, "%02x", buf[i]); if ((i+1) % 4 == 0) fprintf(stderr, " "); } fprintf(stderr, "\n"); } #endif return total; } static void gc3355_send_cmds(struct cgpu_info *gridseed, const char *cmds[]) { unsigned char ob[512]; int i; for(i=0; cmds[i] != NULL; i++) { hex2bin(ob, cmds[i], sizeof(ob)); gc3355_write(gridseed, ob, strlen(cmds[i])/2); } } static void gc3355_send_cmds_bin(struct cgpu_info *gridseed, const char *cmds[], int size) { int i; for(i=0; cmds[i] != NULL; i++) { gc3355_write(gridseed, (unsigned char *)cmds[i], size); } } static bool gc3355_read_register(struct cgpu_info *gridseed, uint32_t reg_addr, uint32_t *reg_value) { GRIDSEED_INFO *info = (GRIDSEED_INFO*)(gridseed->device_data); unsigned char cmd[16] = "\x55\xaa\xc0\x01"; uint32_t reg_len = 4; unsigned char buf[4]; if (info->fw_version != 0x01140113) { applog(LOG_ERR, "Can't read registers; incompatible firmware %08X on %s%d", info->fw_version, gridseed->drv->name, gridseed->device_id); return false; } *(uint32_t *)(cmd + 4) = htole32(reg_addr); *(uint32_t *)(cmd + 8) = htole32(reg_len); *(uint32_t *)(cmd + 12) = htole32(reg_len); if (gc3355_write(gridseed, cmd, sizeof(cmd)) != sizeof(cmd)) { applog(LOG_DEBUG, "Failed to write data to %s%d", gridseed->drv->name, gridseed->device_id); return false; } if (gc3355_get_data(gridseed, buf, 4) != 4) { applog(LOG_DEBUG, "No response from %s%d", gridseed->drv->name, gridseed->device_id); return false; } *reg_value = le32toh(*(uint32_t *)buf); return true; } static bool gc3355_write_register(struct cgpu_info *gridseed, uint32_t reg_addr, uint32_t reg_value) { GRIDSEED_INFO *info = (GRIDSEED_INFO*)(gridseed->device_data); unsigned char cmd[16] = "\x55\xaa\xc0\x02"; uint32_t reg_len = 4; unsigned char buf[4]; if (info->fw_version != 0x01140113) { applog(LOG_ERR, "Can't write registers; incompatible firmware %08X on %s%d", info->fw_version, gridseed->drv->name, gridseed->device_id); return false; } *(uint32_t *)(cmd + 4) = htole32(reg_addr); *(uint32_t *)(cmd + 8) = htole32(reg_value); *(uint32_t *)(cmd + 12) = htole32(reg_len); if (gc3355_write(gridseed, cmd, sizeof(cmd)) != sizeof(cmd)) { applog(LOG_DEBUG, "Failed to write data to %s%d", gridseed->drv->name, gridseed->device_id); return false; } if (gc3355_get_data(gridseed, buf, 4) != 4) { applog(LOG_DEBUG, "No response from %s%d", gridseed->drv->name, gridseed->device_id); return false; } return true; } static void gc3355_switch_leds(struct cgpu_info *gridseed) { uint32_t reg_value; // Set GPIOB pins 0 and 1 as general purpose output, open-drain, 50 MHz max if (!gc3355_read_register(gridseed, GRIDSEED_GPIOB_BASE + GRIDSEED_CRL_OFFSET, ®_value)) { applog(LOG_DEBUG, "Failed to read GPIOA CRL register from %i", gridseed->device_id); return; } reg_value = (reg_value & 0xffffff00) | 0x00000077; if (!gc3355_write_register(gridseed, GRIDSEED_GPIOB_BASE + GRIDSEED_CRL_OFFSET, reg_value)) { applog(LOG_DEBUG, "Failed to write GPIOA CRL register from %i", gridseed->device_id); return; } applog(LOG_NOTICE, "%s%d: Turned off GC3355 LEDs", gridseed->drv->name, gridseed->device_id); } static void gc3355_switch_voltage(struct cgpu_info *gridseed) { uint32_t reg_value; // Put GPIOA pin 5 into general function, 50 MHz output. if (!gc3355_read_register(gridseed, GRIDSEED_GPIOA_BASE + GRIDSEED_CRL_OFFSET, ®_value)) { applog(LOG_DEBUG, "Failed to read GPIOA CRL register from %s%d", gridseed->drv->name, gridseed->device_id); return; } reg_value = (reg_value & 0xff0fffff) | 0x00300000; if (!gc3355_write_register(gridseed, GRIDSEED_GPIOA_BASE + GRIDSEED_CRL_OFFSET, reg_value)) { applog(LOG_DEBUG, "Failed to write GPIOA CRL register from %s%d", gridseed->drv->name, gridseed->device_id); return; } // Set GPIOA pin 5 high. if (!gc3355_read_register(gridseed, GRIDSEED_GPIOA_BASE + GRIDSEED_ODR_OFFSET, ®_value)) { applog(LOG_DEBUG, "Failed to read GPIOA ODR register from %s%d", gridseed->drv->name, gridseed->device_id); return; } reg_value |= 0x00000020; if (!gc3355_write_register(gridseed, GRIDSEED_GPIOA_BASE + GRIDSEED_ODR_OFFSET, reg_value)) { applog(LOG_DEBUG, "Failed to write GPIOA ODR register from %s%d", gridseed->drv->name, gridseed->device_id); return; } applog(LOG_NOTICE, "%s%d: Switched GC3355 voltage to alternate voltage", gridseed->drv->name, gridseed->device_id); } static void gc3355_set_init_nonce(struct cgpu_info *gridseed) { GRIDSEED_INFO *info; int i; char **cmds, *p; uint32_t nonce, step; info = (GRIDSEED_INFO*)(gridseed->device_data); cmds = calloc(sizeof(char*)*(info->chips+1), 1); if (unlikely(!cmds)) quit(1, "Failed to calloc init nonce commands data array"); step = 0xffffffff / info->chips; for(i=0; ichips; i++) { p = calloc(8, 1); if (unlikely(!p)) quit(1, "Failed to calloc init nonce commands data"); memcpy(p, "\x55\xaa\x00\x00", 4); p[2] = i; nonce = htole32(step*i); memcpy(p+4, &nonce, sizeof(nonce)); cmds[i] = p; } cmds[i] = NULL; gc3355_send_cmds_bin(gridseed, (const char **)cmds, 8); for(i=0; ichips; i++) free(cmds[i]); free(cmds); } static void gc3355_enable_btc_cores(struct cgpu_info *gridseed, GRIDSEED_INFO *info) { unsigned char cmd[24], c1, c2; uint16_t mask; int i; mask = 0x00; for(i=0; ibtcore; i++) mask = mask << 1 | 0x01; if (mask == 0) return; c1 = mask & 0x00ff; c2 = mask >> 8; memset(cmd, 0, sizeof(cmd)); memcpy(cmd, "\x55\xAA\xEF\x02", 4); for(i=4; i<24; i++) { cmd[i] = ((i%2)==0) ? c1 : c2; gc3355_write(gridseed, cmd, sizeof(cmd)); } } static void gc3355_set_core_freq(struct cgpu_info *gridseed) { GRIDSEED_INFO *info = (GRIDSEED_INFO*)(gridseed->device_data); gc3355_write(gridseed, info->cmd_freq, sizeof(info->cmd_freq)); if (SHA256_MODE(info->mode)) { gc3355_write(gridseed, info->cmd_btc_baud, sizeof(info->cmd_btc_baud)); } applog(LOG_NOTICE, "%s%d: Set GC3355 core frequency to %d MHz", gridseed->drv->name, gridseed->device_id, info->freq); } static void gc3355_init(struct cgpu_info *gridseed, GRIDSEED_INFO *info) { char buf[512]; int amount; applog(LOG_NOTICE, "%s%d: System reseting", gridseed->drv->name, gridseed->device_id); gc3355_send_cmds(gridseed, str_reset); cgsleep_ms(200); if (using_libusb(info)) { usb_buffer_clear(gridseed); usb_read_timeout(gridseed, buf, sizeof(buf), &amount, 10, C_GETRESULTS); } else { flush_uart(info->device_fd); } gc3355_send_cmds(gridseed, str_init); gc3355_send_cmds(gridseed, str_ltc_reset); gc3355_set_core_freq(gridseed); if (SHA256_MODE(info->mode)) { gc3355_set_init_nonce(gridseed); gc3355_enable_btc_cores(gridseed, info); if (info->usefifo == 0) gc3355_send_cmds(gridseed, str_nofifo); } if (info->voltage) gc3355_switch_voltage(gridseed); if (info->led) gc3355_switch_leds(gridseed); } static void set_freq_cmd(GRIDSEED_INFO *info, int pll_r, int pll_f, int pll_od) { if (pll_r == 0 && pll_f == 0 && pll_od == 0) { // Support frequency increments of 12.5 MHz // Non-integer frequencies must be specified rounded up // With these values the minimum we can set is 12.5 and the max 1600 pll_r = 1; pll_f = 2 * info->freq / GRIDSEED_F_IN - 1; pll_f = MAX(0, MIN(127, pll_f)); } double f_ref = GRIDSEED_F_IN / (pll_r + 1.); double f_vco = f_ref * (pll_f + 1.); double f_out = f_vco / (1 << pll_od); int pll_bs = (f_out >= 500.) ? 1 : 0; int cfg_pm = 1, pll_clk_gate = 1; uint32_t cmdf = (cfg_pm << 0) | (pll_clk_gate << 2) | (pll_r << 16) | (pll_f << 21) | (pll_od << 28) | (pll_bs << 31); info->freq = (int)ceil(f_out); memcpy(info->cmd_freq, "\x55\xaa\xef\x00", 4); // *(uint32_t *)(info->cmd_freq + 4) = htole32(cmdf); cmdf = htole32(cmdf); memcpy(info->cmd_freq + 4, &cmdf, 4); uint32_t cmdb = 0x40000000 | ((uint32_t)round(f_out * 1000000. / info->baud) & 0xffff); cmdb = htole32(cmdb); memcpy(info->cmd_btc_baud, "\x55\xaa\x0f\xff", 4); memcpy(info->cmd_btc_baud + 4, &cmdb, 4); } static bool get_options(GRIDSEED_INFO *info, const char *options) { char *ss, *p, *end, *comma, *eq; int tmp, pll_r = 0, pll_f = 0, pll_od = 0; if (options == NULL) return false; //applog(LOG_NOTICE, "GridSeed options: '%s'", options); ss = strdup(options); p = ss; end = p + strlen(p); another: comma = strchr(p, ','); if (comma != NULL) *comma = '\0'; eq = strchr(p, '='); if (eq == NULL) goto next; *eq = '\0'; tmp = atoi(eq+1); if (strcasecmp(p, "baud")==0) { info->baud = (tmp != 0) ? tmp : info->baud; } else if (strcasecmp(p, "freq")==0) { info->freq = tmp; } else if (strcasecmp(p, "pll_r")==0) { pll_r = (tmp != 0) ? tmp : pll_r; pll_r = MAX(0, MIN(31, pll_r)); } else if (strcasecmp(p, "pll_f")==0) { pll_f = (tmp != 0) ? tmp : pll_f; pll_f = MAX(0, MIN(127, pll_f)); } else if (strcasecmp(p, "pll_od")==0) { pll_od = (tmp != 0) ? tmp : pll_od; pll_od = MAX(0, MIN(4, pll_od)); } else if (strcasecmp(p, "chips")==0) { info->chips = (tmp != 0) ? tmp : info->chips; info->chips = MAX(0, MIN(GRIDSEED_MAX_CHIPS, info->chips)); } else if (strcasecmp(p, "modules")==0) { info->modules = (tmp != 0) ? tmp : info->modules; } else if (strcasecmp(p, "usefifo")==0) { info->usefifo = tmp; } else if (strcasecmp(p, "btc")==0) { info->btcore = tmp; } else if (strcasecmp(p, "voltage")==0) { info->voltage = (tmp != 0) ? tmp : info->voltage; } else if (strcasecmp(p, "led_off")==0) { info->led = (tmp != 0) ? tmp : info->led; } else if (strcasecmp(p, "per_chip_stats")==0) { info->per_chip_stats = (tmp != 0) ? tmp : info->per_chip_stats; } else if (strcasecmp(p, "start_port")==0) { info->ltc_port = tmp; } next: if (comma != NULL) { p = comma + 1; if (p < end) goto another; } free(ss); set_freq_cmd(info, pll_r, pll_f, pll_od); return true; } static bool get_freq(GRIDSEED_INFO *info, const char *options, const char *id) { char *ss, *p, *end, *comma, *eq; int tmp; if (options == NULL) return false; applog(LOG_NOTICE, "GridSeed freq options: '%s'", options); ss = strdup(options); p = ss; end = p + strlen(p); another: comma = strchr(p, ','); if (comma != NULL) *comma = '\0'; eq = strchr(p, '='); if (eq == NULL) goto next; *eq = '\0'; tmp = atoi(eq+1); if (strcasecmp(p, id) == 0) { info->freq = tmp; set_freq_cmd(info, 0, 0, 0); if (info->freq == tmp) applog(LOG_INFO, "%s unique frequency: %i MHz", p, info->freq); else applog(LOG_NOTICE, "%s unique frequency: requested %i MHz, using instead %i MHz", p, tmp, info->freq); } next: if (comma != NULL) { p = comma + 1; if (p < end) goto another; } free(ss); return true; } static bool get_override(GRIDSEED_INFO *info, const char *options, const char *id) { char *ss, *p, *colon, *semi; bool ret = false; if (options == NULL) return false; ss = strdup(options); p = ss; do { semi = strchr(p, ';'); if (semi != NULL) *semi = '\0'; colon = strchr(p, ':'); if (colon == NULL) continue; *colon = '\0'; if (strcasecmp(p, id) == 0) { ret = get_options(info, colon + 1); break; } } while (semi != NULL && (p = semi + 1)); free(ss); return ret; } static int gridseed_cp210x_init(struct cgpu_info *gridseed, int interface) { // Enable the UART transfer(gridseed, CP210X_TYPE_OUT, CP210X_REQUEST_IFC_ENABLE, CP210X_VALUE_UART_ENABLE, interface, C_ENABLE_UART); if (gridseed->usbinfo.nodev) return -1; // Set data control transfer(gridseed, CP210X_TYPE_OUT, CP210X_REQUEST_DATA, CP210X_VALUE_DATA, interface, C_SETDATA); if (gridseed->usbinfo.nodev) return -1; // Set the baud uint32_t data = CP210X_DATA_BAUD; _transfer(gridseed, CP210X_TYPE_OUT, CP210X_REQUEST_BAUD, 0, interface, &data, sizeof(data), C_SETBAUD); return 0; } static int gridseed_ftdi_init(struct cgpu_info *gridseed, int interface) { int err; // Reset err = usb_transfer(gridseed, FTDI_TYPE_OUT, FTDI_REQUEST_RESET, FTDI_VALUE_RESET, interface, C_RESET); applog(LOG_DEBUG, "%s%i: reset got err %d", gridseed->drv->name, gridseed->device_id, err); if (gridseed->usbinfo.nodev) return -1; // Set latency err = usb_transfer(gridseed, FTDI_TYPE_OUT, FTDI_REQUEST_LATENCY, GRIDSEED_LATENCY, interface, C_LATENCY); applog(LOG_DEBUG, "%s%i: latency got err %d", gridseed->drv->name, gridseed->device_id, err); if (gridseed->usbinfo.nodev) return -1; // Set data err = usb_transfer(gridseed, FTDI_TYPE_OUT, FTDI_REQUEST_DATA, FTDI_VALUE_DATA_AVA, interface, C_SETDATA); applog(LOG_DEBUG, "%s%i: data got err %d", gridseed->drv->name, gridseed->device_id, err); if (gridseed->usbinfo.nodev) return -1; // Set the baud err = usb_transfer(gridseed, FTDI_TYPE_OUT, FTDI_REQUEST_BAUD, FTDI_VALUE_BAUD_AVA, (FTDI_INDEX_BAUD_AVA & 0xff00) | interface, C_SETBAUD); applog(LOG_DEBUG, "%s%i: setbaud got err %d", gridseed->drv->name, gridseed->device_id, err); if (gridseed->usbinfo.nodev) return -1; // Set Modem Control err = usb_transfer(gridseed, FTDI_TYPE_OUT, FTDI_REQUEST_MODEM, FTDI_VALUE_MODEM, interface, C_SETMODEM); applog(LOG_DEBUG, "%s%i: setmodemctrl got err %d", gridseed->drv->name, gridseed->device_id, err); if (gridseed->usbinfo.nodev) return -1; // Set Flow Control err = usb_transfer(gridseed, FTDI_TYPE_OUT, FTDI_REQUEST_FLOW, FTDI_VALUE_FLOW, interface, C_SETFLOW); applog(LOG_DEBUG, "%s%i: setflowctrl got err %d", gridseed->drv->name, gridseed->device_id, err); if (gridseed->usbinfo.nodev) return -1; /* Avalon repeats the following */ // Set Modem Control err = usb_transfer(gridseed, FTDI_TYPE_OUT, FTDI_REQUEST_MODEM, FTDI_VALUE_MODEM, interface, C_SETMODEM); applog(LOG_DEBUG, "%s%i: setmodemctrl 2 got err %d", gridseed->drv->name, gridseed->device_id, err); if (gridseed->usbinfo.nodev) return -1; // Set Flow Control err = usb_transfer(gridseed, FTDI_TYPE_OUT, FTDI_REQUEST_FLOW, FTDI_VALUE_FLOW, interface, C_SETFLOW); applog(LOG_DEBUG, "%s%i: setflowctrl 2 got err %d", gridseed->drv->name, gridseed->device_id, err); if (gridseed->usbinfo.nodev) return -1; return 0; } static int gridseed_pl2303_init(struct cgpu_info *gridseed, int interface) { // Set Data Control transfer(gridseed, PL2303_CTRL_OUT, PL2303_REQUEST_CTRL, PL2303_VALUE_CTRL, interface, C_SETDATA); if (gridseed->usbinfo.nodev) return -1; // Set Line Control uint32_t ica_data[2] = { PL2303_VALUE_LINE0, PL2303_VALUE_LINE1 }; _transfer(gridseed, PL2303_CTRL_OUT, PL2303_REQUEST_LINE, PL2303_VALUE_LINE, interface, &ica_data[0], PL2303_VALUE_LINE_SIZE, C_SETLINE); if (gridseed->usbinfo.nodev) return -1; // Vendor transfer(gridseed, PL2303_VENDOR_OUT, PL2303_REQUEST_VENDOR, PL2303_VALUE_VENDOR, interface, C_VENDOR); return 0; } static int gridseed_initialise_usb(struct cgpu_info *gridseed, GRIDSEED_INFO *info) { int err, interface; if (gridseed->usbinfo.nodev) return -1; interface = usb_interface(gridseed); info->ident = usb_ident(gridseed); switch(info->ident) { case IDENT_GSD: err = 0; break; case IDENT_GSD1: err = gridseed_cp210x_init(gridseed, interface); break; case IDENT_GSD2: err = gridseed_ftdi_init(gridseed, interface); break; case IDENT_GSD3: err = gridseed_pl2303_init(gridseed, interface); break; default: err = -1; applog(LOG_DEBUG, "gridseed_intialise() called with invalid %s cgid %i ident=%d", gridseed->drv->name, gridseed->cgminer_id, info->ident); } return err; } static struct cgpu_info *gridseed_detect_one_scrypt_proxy() { struct cgpu_info *gridseed; GRIDSEED_INFO *info; gridseed = calloc(1, sizeof(struct cgpu_info)); if (unlikely(!gridseed)) quit(1, "Failed to calloc struct cgpu_info"); info = calloc(1, sizeof(GRIDSEED_INFO)); if (unlikely(!info)) quit(1, "Failed to calloc struct GRIDSEED_INFO"); gridseed->drv = &gridseed_drv; gridseed->device_data = info; gridseed->deven = DEV_ENABLED; gridseed->threads = GRIDSEED_MINER_THREADS; info->device_fd = -1; info->using_libusb = 0; info->mode = MODE_SCRYPT_DUAL; info->baud = GRIDSEED_DEFAULT_BAUD; info->freq = GRIDSEED_DEFAULT_FREQUENCY; info->chips = GRIDSEED_DEFAULT_CHIPS; info->modules = GRIDSEED_DEFAULT_MODULES; info->usefifo = GRIDSEED_DEFAULT_USEFIFO; info->btcore = GRIDSEED_DEFAULT_BTCORE; info->voltage = 0; info->led = 0; info->per_chip_stats = 0; memset(info->nonce_count, 0, sizeof(info->nonce_count)); memset(info->error_count, 0, sizeof(info->error_count)); set_freq_cmd(info, 0, 0, 0); info->sockltc = -1; info->ltc_port = GRIDSEED_PROXY_PORT; cgsem_init(&info->psem); get_options(info, opt_gridseed_options); if (!gridseed_find_proxy(info)) goto unallocall; if (!add_cgpu(gridseed)) goto unallocall; return gridseed; unallocall: free(gridseed->device_data); gridseed->device_data = NULL; free(gridseed); return NULL; } static bool gridseed_detect_one_common(struct cgpu_info *gridseed) { GRIDSEED_INFO *info = gridseed->device_data; unsigned char rbuf[GRIDSEED_READ_SIZE]; const char detect_cmd[] = "55aac000909090900000000001000000"; unsigned char detect_data[16]; if (opt_scrypt) info->mode = MODE_SCRYPT; else info->mode = MODE_SHA256; info->baud = GRIDSEED_DEFAULT_BAUD; info->freq = GRIDSEED_DEFAULT_FREQUENCY; info->chips = GRIDSEED_DEFAULT_CHIPS; info->modules = GRIDSEED_DEFAULT_MODULES; info->usefifo = GRIDSEED_DEFAULT_USEFIFO; info->btcore = GRIDSEED_DEFAULT_BTCORE; info->voltage = 0; info->led = 0; info->per_chip_stats = 0; memset(info->nonce_count, 0, sizeof(info->nonce_count)); memset(info->error_count, 0, sizeof(info->error_count)); set_freq_cmd(info, 0, 0, 0); info->sockltc = -1; info->ltc_port = GRIDSEED_PROXY_PORT; cgsem_init(&info->psem); get_options(info, opt_gridseed_options); get_freq(info, opt_gridseed_freq, gridseed->unique_id); get_override(info, opt_gridseed_override, gridseed->unique_id); /* get MCU firmware version */ hex2bin(detect_data, detect_cmd, sizeof(detect_data)); if (gc3355_write(gridseed, detect_data, sizeof(detect_data)) != sizeof(detect_data)) { applog(LOG_DEBUG, "Failed to write detect command to gridseed device"); return false; } /* waiting for return */ if (gc3355_get_data(gridseed, rbuf, GRIDSEED_READ_SIZE) != GRIDSEED_READ_SIZE) { applog(LOG_DEBUG, "No response from gridseed device"); return false; } if (memcmp(rbuf, "\x55\xaa\xc0\x00\x90\x90\x90\x90", GRIDSEED_READ_SIZE-4) != 0) { applog(LOG_DEBUG, "Bad response from gridseed device"); return false; } if (!add_cgpu(gridseed)) return false; info->fw_version = le32toh(*(uint32_t *)(rbuf+GRIDSEED_READ_SIZE-4)); applog(LOG_NOTICE, "Gridseed device found, firmware v%08X, driver %s, ID %s", info->fw_version, gridseed_version, gridseed->unique_id); gc3355_init(gridseed, info); return true; } static struct cgpu_info *gridseed_detect_one_usb(struct libusb_device *dev, struct usb_find_devices *found) { struct cgpu_info *gridseed; GRIDSEED_INFO *info; gridseed = usb_alloc_cgpu(&gridseed_drv, GRIDSEED_MINER_THREADS); if (!usb_init(gridseed, dev, found)) goto shin; libusb_reset_device(gridseed->usbdev->handle); info = calloc(1, sizeof(GRIDSEED_INFO)); if (unlikely(!info)) quit(1, "Failed to calloc struct GRIDSEED_INFO"); update_usb_stats(gridseed); gridseed->device_data = info; info->device_fd = -1; info->using_libusb = 1; gridseed->unique_id = gridseed->usbdev->serial_string; strncpy(info->id, gridseed->unique_id, sizeof(info->id)); info->id[sizeof(info->id) - 1] = '\0'; gridseed->usbdev->usb_type = USB_TYPE_STD; if (gridseed_initialise_usb(gridseed, info)) { applog(LOG_ERR, "Failed to initialize gridseed device"); goto unshin; } if (gridseed_detect_one_common(gridseed)) return gridseed; unshin: usb_uninit(gridseed); free(gridseed->device_data); gridseed->device_data = NULL; shin: gridseed = usb_free_cgpu(gridseed); return NULL; } static bool gridseed_detect_one_serial(const char *devpath) { struct cgpu_info *gridseed; GRIDSEED_INFO *info; int fd; if (initial_startup_phase) applog(LOG_INFO, "Gridseed Detect: Attempting to open %s", devpath); fd = gridseed_serial_open_detect(devpath, GRIDSEED_DEFAULT_BAUD, true); if (unlikely(fd == -1)) { if (initial_startup_phase) applog(LOG_ERR, "Gridseed Detect: Failed to open %s", devpath); return false; } flush_uart(fd); gridseed = calloc(1, sizeof(struct cgpu_info)); if (unlikely(!gridseed)) quit(1, "Failed to calloc struct cgpu_info"); info = calloc(1, sizeof(GRIDSEED_INFO)); if (unlikely(!info)) quit(1, "Failed to calloc struct GRIDSEED_INFO"); gridseed->drv = &gridseed_drv; gridseed->device_path = strdup(devpath); gridseed->device_data = info; gridseed->deven = DEV_ENABLED; gridseed->threads = GRIDSEED_MINER_THREADS; info->device_fd = fd; info->using_libusb = 0; gridseed->unique_id = MAX( strrchr(gridseed->device_path, '/'), strrchr(gridseed->device_path, '\\')); if (gridseed->unique_id == NULL) gridseed->unique_id = gridseed->device_path; else ++gridseed->unique_id; strncpy(info->id, gridseed->unique_id, sizeof(info->id)); info->id[sizeof(info->id) - 1] = '\0'; if (gridseed_detect_one_common(gridseed)) return true; free(gridseed->device_data); gridseed->device_data = NULL; free(gridseed); return false; } static bool gridseed_send_query_cmd(struct cgpu_info *gridseed, GRIDSEED_INFO *info) { unsigned char *cmd = (unsigned char *)"\x55\xaa\xc0\x00\xa0\xa0\xa0\xa0\x00\x00\x00\x00\x01\x00\x00\x00"; cgtimer_t ts_now, ts_res; bool ret = false; cgtimer_time(&ts_now); mutex_lock(&info->qlock); if (!info->query_qlen) { cgtimer_sub(&ts_now, &info->query_ts, &ts_res); #ifndef WIN32 if (ts_res.tv_sec > 0) { #else if (ts_res.QuadPart > 10000000) { #endif if (gc3355_write(gridseed, cmd, 16) == 16) { info->query_qlen = true; ret = true; } } } mutex_unlock(&info->qlock); return ret; } #define SHA256_TASK_LEN 52 #define SCRYPT_TASK_LEN 156 static bool gridseed_send_work_usb(struct cgpu_info *gridseed, unsigned char *target, unsigned char *midstate, unsigned char *data, int workid, enum gsd_mode mode) { unsigned char cmd[ MAX(SHA256_TASK_LEN, SCRYPT_TASK_LEN) ]; int ret; if (SHA256_MODE(mode)) { memcpy(cmd, "\x55\xaa\x0f\x01", 4); memcpy(cmd+4, midstate, 32); memcpy(cmd+36, data+64, 12); memcpy(cmd+48, &(workid), 4); } else { gc3355_send_cmds(gridseed, str_ltc_reset); cgsleep_ms(50); memcpy(cmd, "\x55\xaa\x1f\x00", 4); memcpy(cmd+4, target, 32); memcpy(cmd+36, midstate, 32); memcpy(cmd+68, data, 80); memcpy(cmd+148, "\xff\xff\xff\xff", 4); // nonce_max memcpy(cmd+152, &(workid), 4); // taskid } ret = gc3355_write(gridseed, cmd, (SHA256_MODE(mode)) ? SHA256_TASK_LEN : SCRYPT_TASK_LEN); return (ret == (SHA256_MODE(mode)) ? SHA256_TASK_LEN : SCRYPT_TASK_LEN); } static bool gridseed_send_work(struct cgpu_info *gridseed, GRIDSEED_INFO *info, struct work *work) { if (info->mode == MODE_SCRYPT_DUAL) return gridseed_send_work_packet(info, work); else return gridseed_send_work_usb(gridseed, work->target, work->midstate, work->data, work->id, info->mode); } static void gridseed_get_queue_length(GRIDSEED_INFO *info, unsigned char *data) { uint32_t qlen; memcpy(&qlen, data+8, 4); qlen = htole32(qlen); mutex_lock(&info->qlock); info->query_qlen = false; info->dev_queue_len = GRIDSEED_MCU_QUEUE_LEN - qlen; info->needworks = qlen; cgtimer_time(&info->query_ts); mutex_unlock(&info->qlock); } static void gridseed_parse_mcu_command(struct cgpu_info *gridseed, GRIDSEED_INFO *info, unsigned char *data) { if (memcmp(data+4, "\xa0\xa0\xa0\xa0", 4) == 0) { /* FIFO idle slots */ gridseed_get_queue_length(info, data); } else if (memcmp(data+4, "\x4d\x43\x55\x52\x45\x53\x45\x54", 8) == 0) { /* MCU watchdog did HW reset. Re-init GC3355 chips */ gc3355_init(gridseed, info); } } static void __gridseed_purge_sha_work_queue(struct cgpu_info *gridseed, GRIDSEED_INFO *info, int newstart) { int i; if (newstart <= 0 || newstart >= info->soft_queue_len) return; for(i=0; iworkqueue[i]); info->workdone++; } memmove(&(info->workqueue[0]), &(info->workqueue[newstart]), sizeof(struct work*)*(info->soft_queue_len - newstart)); info->soft_queue_len -= newstart; } static void __maybe_unused gridseed_purge_sha_work_queue(struct cgpu_info *gridseed, GRIDSEED_INFO *info, int newstart) { mutex_lock(&info->qlock); __gridseed_purge_sha_work_queue(gridseed, info, newstart); mutex_unlock(&info->qlock); } static void __gridseed_purge_scrypt_work(GRIDSEED_INFO *info) { if (info->ltc_work != NULL) { free_work(info->ltc_work); info->ltc_work = NULL; } } static void gridseed_purge_scrypt_work(GRIDSEED_INFO *info) { mutex_lock(&info->qlock); __gridseed_purge_scrypt_work(info); mutex_unlock(&info->qlock); } static void gridseed_test_btc_nonce(struct cgpu_info *gridseed, GRIDSEED_INFO *info, struct thr_info *thr, unsigned char *data) { struct work *work; uint32_t nonce, chip; unsigned int workid; int index, i; bool valid = false; bool nowork = false; memcpy(&workid, data+8, 4); memcpy(&nonce, data+4, 4); nonce = htole32(nonce); chip = nonce / (0xffffffff / info->chips); mutex_lock(&info->qlock); nowork = (info->soft_queue_len <= 0); for(i=0; isoft_queue_len; i++) { struct work *dupwork; work = info->workqueue[i]; if (work->devflag == false) continue; if (work->id > workid) break; dupwork = copy_work(work); if (dupwork == NULL) continue; if (test_nonce(dupwork, nonce)) { submit_tested_work(thr, dupwork); index = i; valid = true; free_work(dupwork); break; } else free_work(dupwork); } if (valid) __gridseed_purge_sha_work_queue(gridseed, info, index); info->nonce_count[chip]++; if (!valid && !nowork) info->error_count[chip]++; mutex_unlock(&info->qlock); if (!valid && !nowork) inc_hw_errors(thr); } static void __gridseed_test_ltc_nonce(struct cgpu_info *gridseed, GRIDSEED_INFO *info, struct thr_info *thr, uint32_t nonce, unsigned int workid) { struct timeval tv_workend; uint32_t chip = nonce / (0xffffffff / info->chips); bool valid = false; cgtime(&tv_workend); mutex_lock(&info->qlock); if (info->ltc_work == NULL) { /* Work was flushed while the GSD was hashing, * * and no new work has been sent yet. */ applog(LOG_DEBUG, "%s%d sent nonce for flushed work", gridseed->drv->name, gridseed->device_id); mutex_unlock(&info->qlock); return; } /* if (work->devflag == false) { applog(LOG_ERR, "gridseed_test_ltc_nonce called but work hasn't been sent (G:%d L:%d F:%d)", workid, info->last_work_id, info->flushed); mutex_unlock(&info->qlock); return; } */ if (info->ltc_work->id != workid) { /* Work was flushed and new work assigned * * just as the GSD reported its result. */ applog(LOG_DEBUG, "%s%d sent nonce for old work", gridseed->drv->name, gridseed->device_id); mutex_unlock(&info->qlock); return; } if (test_nonce(info->ltc_work, nonce)) { submit_tested_work(thr, info->ltc_work); valid = true; } info->workdone++; info->hashes_per_ms = (nonce % (0xffffffff / info->chips)) * info->chips / ms_tdiff(&tv_workend, &info->ltc_workstart); __gridseed_purge_scrypt_work(info); info->nonce_count[chip]++; if (!valid) info->error_count[chip]++; mutex_unlock(&info->qlock); if (!valid) inc_hw_errors(thr); } static void gridseed_test_ltc_nonce(struct cgpu_info *gridseed, GRIDSEED_INFO *info, struct thr_info *thr, unsigned char *data) { uint32_t nonce; unsigned int workid; memcpy(&workid, data+8, 4); memcpy(&nonce, data+4, 4); nonce = htole32(nonce); __gridseed_test_ltc_nonce(gridseed, info, thr, nonce, workid); } static void gridseed_parse_response(struct cgpu_info *gridseed, GRIDSEED_INFO *info, struct thr_info *thr, unsigned char *readbuf, int *offset) { unsigned char *p; int size; p = readbuf; size = *offset; one_cmd: /* search the starting 0x55 */ while(size > 0) { if (likely(*p == 0x55)) break; p++; size--; } if (size < GRIDSEED_READ_SIZE) goto out_cmd; switch(p[1]) { case 0xaa: /* Queue length result */ gridseed_parse_mcu_command(gridseed, info, p); break; case 0x10: /* BTC result */ gridseed_test_btc_nonce(gridseed, info, thr, p); break; case 0x20: /* LTC result */ if (SHA256_MODE(info->mode)) { gridseed_send_nonce_packet(info, p); } else if (SCRYPT_MODE(info->mode)) { gridseed_test_ltc_nonce(gridseed, info, thr, p); } break; default: applog(LOG_ERR, "%s%d: Received unknown response", gridseed->drv->name, gridseed->device_id); break; } p += GRIDSEED_READ_SIZE; size -= GRIDSEED_READ_SIZE; goto one_cmd; out_cmd: if (size > 0) memmove(readbuf, p, size); *offset = size; } static bool gridseed_check_new_btc_task(GRIDSEED_INFO *info) { cgtimer_t ts_now, ts_res; bool ret = false; cgtimer_time(&ts_now); mutex_lock(&info->qlock); cgtimer_sub(&ts_now, &info->query_ts, &ts_res); #ifndef WIN32 if (ts_res.tv_sec > 0 || ts_res.tv_nsec > 350000000) { #else if (ts_res.QuadPart > 3500000) { #endif info->query_qlen = false; info->dev_queue_len = 1; info->needworks = 1; cgtimer_time(&info->query_ts); ret = true; } mutex_unlock(&info->qlock); return ret; } static bool gridseed_check_new_ltc_work(GRIDSEED_INFO *info) { struct thr_info *thr = info->thr; struct work *work; const int thr_id = thr->id; bool need_work; need_work = (info->ltc_work == NULL); if (need_work) { work = get_work(thr, thr_id); mutex_lock(&info->qlock); if (info->ltc_work == NULL) { work->devflag = false; info->ltc_work = work; } else { need_work = false; } mutex_unlock(&info->qlock); if (!need_work) discard_work(work); } return need_work; } /* * Thread to read response from Miner device */ static void *gridseed_recv_usb(void *userdata) { struct cgpu_info *gridseed = (struct cgpu_info *)userdata; GRIDSEED_INFO *info = gridseed->device_data; struct thr_info *thr = info->thr; char threadname[24]; unsigned char readbuf[GRIDSEED_READBUF_SIZE]; int offset = 0, amount; snprintf(threadname, sizeof(threadname), "GridSeed_Recv/%d", gridseed->device_id); RenameThread(threadname); applog(LOG_INFO, "GridSeed: recv thread running, %s", threadname); while (likely(!gridseed->shutdown)) { if (unlikely(using_libusb(info) && gridseed->usbinfo.nodev)) break; if (unlikely(info->serial_reopen)) { cgsleep_ms(500); continue; } amount = gc3355_read(gridseed, readbuf + offset, GRIDSEED_READ_SIZE, 1); if (amount < 0) continue; offset += amount; if (offset >= GRIDSEED_READ_SIZE) gridseed_parse_response(gridseed, info, thr, readbuf, &offset); if (unlikely(offset + GRIDSEED_READ_SIZE >= GRIDSEED_READBUF_SIZE)) { applog(LOG_ERR, "%s%d: Read buffer overflow, resetting", gridseed->drv->name, gridseed->device_id); offset = 0; } } gridseed->shutdown = true; return NULL; } /* * Thread to send task and queue length query command to device */ static void *gridseed_send(void *userdata) { struct cgpu_info *gridseed = (struct cgpu_info *)userdata; GRIDSEED_INFO *info = gridseed->device_data; char threadname[24]; int i; snprintf(threadname, sizeof(threadname), "GridSeed_Send/%d", gridseed->device_id); RenameThread(threadname); applog(LOG_INFO, "GridSeed: send thread running, %s", threadname); while (likely(!gridseed->shutdown)) { if (unlikely(using_libusb(info) && gridseed->usbinfo.nodev)) break; if (unlikely(info->serial_reopen)) { if (using_serial(info) && !gridseed_reopen(gridseed)) { applog(LOG_ERR, "Failed to reopen %s%d on %s, shutting down", gridseed->drv->name, gridseed->device_id, gridseed->device_path); break; } info->serial_reopen = false; } if (SHA256_MODE(info->mode)) { cgsleep_ms(50); if (info->usefifo == 0) { /* mark the first work in queue as complete after several ms */ if (gridseed_check_new_btc_task(info)) continue; } else { /* send query command to device */ if (gridseed_send_query_cmd(gridseed, info)) continue; } /* send task to device */ mutex_lock(&info->qlock); for(i=0; isoft_queue_len; i++) { if (info->workqueue[i] && info->workqueue[i]->devflag == false) { if (gridseed_send_work(gridseed, info, info->workqueue[i])) { info->workqueue[i]->devflag = true; break; } } } mutex_unlock(&info->qlock); } else { cgsleep_ms(100); if (!gridseed_check_new_ltc_work(info)) continue; /* send task to device */ mutex_lock(&info->qlock); if (info->ltc_work != NULL && !info->ltc_work->devflag && gridseed_send_work(gridseed, info, info->ltc_work)) { info->ltc_work->devflag = true; cgtime(&info->ltc_workstart); } mutex_unlock(&info->qlock); } } return NULL; } /*========== functions for struct device_drv ===========*/ static int64_t gridseed_scanwork_sha(struct thr_info *); static int64_t gridseed_scanwork_scrypt(struct thr_info *); static int gridseed_autoscan() { applog(LOG_DEBUG, "gridseed_autoscan() called"); return serial_autodetect_udev(gridseed_detect_one_serial, GRIDSEED_USB_ID_MODEL_STR); } static void gridseed_detect(bool __maybe_unused hotplug) { static int serial_usb = 0; if (initial_startup_phase && hotplug) initial_startup_phase = false; // -1 : USB , 1 : Serial if (serial_usb == 0) serial_usb = (list_empty(&scan_devices)) ? -1 : 1; if (serial_usb < 0) usb_detect(&gridseed_drv, gridseed_detect_one_usb); else serial_detect_iauto(&gridseed_drv, gridseed_detect_one_serial, gridseed_autoscan); if (!total_devices && opt_scrypt) while (gridseed_detect_one_scrypt_proxy()) {} } static bool gridseed_prepare(struct thr_info *thr) { applog(LOG_DEBUG, "Entering %s", __FUNCTION__); struct cgpu_info *gridseed = thr->cgpu; GRIDSEED_INFO *info = gridseed->device_data; info->thr = thr; mutex_init(&info->lock); mutex_init(&info->qlock); switch (info->mode) { case MODE_SHA256: case MODE_SHA256_DUAL: gridseed_drv.hash_work = hash_queued_work; gridseed_drv.scanwork = gridseed_scanwork_sha; info->queued = 0; info->dev_queue_len = GRIDSEED_MCU_QUEUE_LEN; info->soft_queue_len = 0; info->needworks = 0; info->query_qlen = false; cgtimer_time(&info->query_ts); memset(&info->workqueue, 0, sizeof(struct work *)*GRIDSEED_SOFT_QUEUE_LEN); info->workdone = 0; gridseed_create_proxy(gridseed, info); break; case MODE_SCRYPT: case MODE_SCRYPT_DUAL: gridseed_drv.hash_work = hash_driver_work; gridseed_drv.scanwork = gridseed_scanwork_scrypt; info->workdone = 0; info->ltc_work = NULL; info->hashes_per_ms = GRIDSEED_HASH_SPEED * (double)(info->freq * info->chips * info->modules); cgtime(&info->scanhash_time); break; default: applog(LOG_ERR, "Gridseed driver could not determine SHA or Scrypt mode"); return false; } if (info->mode == MODE_SCRYPT_DUAL) applog(LOG_NOTICE, "Gridseed %s%d opened on %d/UDP", gridseed->drv->name, gridseed->device_id, info->ltc_port); else applog(LOG_NOTICE, "GridSeed %s%d opened on usb:%s", gridseed->drv->name, gridseed->device_id, gridseed->device_path); return true; } static bool gridseed_thread_init(struct thr_info *thr) { applog(LOG_DEBUG, "Entering %s", __FUNCTION__); struct cgpu_info *gridseed = thr->cgpu; GRIDSEED_INFO *info = gridseed->device_data; if (info->mode != MODE_SCRYPT_DUAL) { if (pthread_create(&info->th_read, NULL, gridseed_recv_usb, (void*)gridseed)) { applog(LOG_ERR, "%s%d: Failed to create read thread", gridseed->drv->name, gridseed->device_id); return false; } } if (pthread_create(&info->th_send, NULL, gridseed_send, (void*)gridseed)) { applog(LOG_ERR, "%s%d: Failed to create send thread", gridseed->drv->name, gridseed->device_id); return false; } if (info->sockltc != -1) { if (pthread_create(&info->th_packet, NULL, gridseed_recv_packet, (void*)gridseed)) { applog(LOG_ERR, "%s%d: Failed to create packet thread", gridseed->drv->name, gridseed->device_id); return -1; } } return true; } static bool gridseed_full(struct cgpu_info *gridseed) { GRIDSEED_INFO *info = gridseed->device_data; struct work *work; int subid; bool ret = true; mutex_lock(&info->qlock); if (info->needworks <= 0) goto out_unlock; work = get_queued(gridseed); if (unlikely(!work)) { ret = false; goto out_unlock; } subid = info->queued++; work->subid = subid; work->devflag = false; /* true when send to device */ if (info->soft_queue_len >= GRIDSEED_SOFT_QUEUE_LEN) __gridseed_purge_sha_work_queue(gridseed, info, 1); info->workqueue[info->soft_queue_len++] = work; info->needworks--; ret = (info->needworks <= 0); out_unlock: mutex_unlock(&info->qlock); return ret; } static int64_t gridseed_scanwork_sha(struct thr_info *thr) { struct cgpu_info *gridseed = thr->cgpu; GRIDSEED_INFO *info = gridseed->device_data; int64_t hashs; cgsleep_ms(100); mutex_lock(&info->qlock); hashs = info->workdone * 0xffffffffL; info->workdone = 0; mutex_unlock(&info->qlock); return hashs; } static int64_t gridseed_scanwork_scrypt(struct thr_info *thr) { struct cgpu_info *gridseed = thr->cgpu; GRIDSEED_INFO *info = gridseed->device_data; struct timeval old_scanhash_time; int64_t elapsed_ms; cgsleep_ms(100); mutex_lock(&info->qlock); old_scanhash_time = info->scanhash_time; cgtime(&info->scanhash_time); elapsed_ms = ms_tdiff(&info->scanhash_time, &old_scanhash_time); mutex_unlock(&info->qlock); return info->hashes_per_ms * elapsed_ms; } #define gridseed_update_work gridseed_flush_work static void gridseed_flush_work(struct cgpu_info *gridseed) { applog(LOG_DEBUG, "Entering %s", __FUNCTION__); GRIDSEED_INFO *info = gridseed->device_data; applog(LOG_INFO, "%s%d: Work updated, flushing work queue", gridseed->drv->name, gridseed->device_id); if (SHA256_MODE(info->mode)) { int i; mutex_lock(&info->qlock); for(i=0; isoft_queue_len; i++) { work_completed(gridseed, info->workqueue[i]); } info->soft_queue_len = 0; mutex_unlock(&info->qlock); } else { gridseed_purge_scrypt_work(info); } } static struct api_data *gridseed_api_stats(struct cgpu_info *gridseed) { applog(LOG_DEBUG, "Entering %s", __FUNCTION__); GRIDSEED_INFO *info = gridseed->device_data; struct api_data *root = NULL; char *mode_str; switch (info->mode) { case MODE_SHA256: mode_str = MODE_SHA256_STR; break; case MODE_SHA256_DUAL: mode_str = MODE_SHA256_DUAL_STR; break; case MODE_SCRYPT: mode_str = MODE_SCRYPT_STR; break; case MODE_SCRYPT_DUAL: mode_str = MODE_SCRYPT_DUAL_STR; break; case MODE_UNK: default: mode_str = MODE_UNK_STR; break; } root = api_add_string(root, "Mode", mode_str, false); root = api_add_string(root, "Serial", info->id, false); root = api_add_int(root, "Frequency", &(info->freq), false); root = api_add_int(root, "Baud", &(info->baud), false); root = api_add_int(root, "Chips", &(info->chips), false); root = api_add_int(root, "BTCore", &(info->btcore), false); root = api_add_int(root, "Modules", &(info->modules), false); root = api_add_int(root, "Use FIFO", &(info->usefifo), false); root = api_add_int(root, "Voltage", &(info->voltage), false); root = api_add_int(root, "Per Chip Stats", &(info->per_chip_stats), false); if (SHA256_MODE(info->mode) || info->mode == MODE_SCRYPT_DUAL) { root = api_add_short(root, "Scrypt Proxy Port", &info->ltc_port, false); } root = api_add_timeval(root, "LTC Workstart", &(info->ltc_workstart), false); return root; } static void gridseed_get_statline_before(char *buf, size_t siz, struct cgpu_info *gridseed) { GRIDSEED_INFO *info = gridseed->device_data; //tailsprintf(buf, siz, "%4d MHz ", info->freq); if (info->mode == MODE_SHA256) tailsprintf(buf, siz, "SHA256"); else if (info->mode == MODE_SHA256_DUAL) tailsprintf(buf, siz, "DUAL / SHA256"); else if (info->mode == MODE_SCRYPT) tailsprintf(buf, siz, "SCRYPT"); else if (info->mode == MODE_SCRYPT_DUAL) tailsprintf(buf, siz, "DUAL / SCRYPT"); tailsprintf(buf, siz, " %4d MHz ", info->freq); //fix typo } static void gridseed_get_statline(char *buf, size_t siz, struct cgpu_info *gridseed) { GRIDSEED_INFO *info = gridseed->device_data; /* With the per_chip_stats option, print the number of nonces * and (if applicable) HW errors per chip at the end of each * device's status line. This however only works for small numbers * of chips, otherwise the information extends off screen and * the string buffer could overflow */ if (info->per_chip_stats && info->chips <= GRIDSEED_DEFAULT_CHIPS * 2) { int i; tailsprintf(buf, siz, " N:"); for (i = 0; i < info->chips; ++i) { tailsprintf(buf, siz, " %d", info->nonce_count[i]); if (info->error_count[i]) tailsprintf(buf, siz, "[%d]", info->error_count[i]); } } } static char *gridseed_set_device(struct cgpu_info *gridseed, char *option, char *setting, char *replybuf) { applog(LOG_DEBUG, "Entering %s", __FUNCTION__); GRIDSEED_INFO *info = gridseed->device_data; int val; if (strcasecmp(option, "help") == 0) { sprintf(replybuf, "freq: range %d-%d", GRIDSEED_MIN_FREQUENCY, GRIDSEED_MAX_FREQUENCY); return replybuf; } if (strcasecmp(option, "freq") == 0) { if (!setting || !*setting) { sprintf(replybuf, "missing freq setting"); return replybuf; } val = atoi(setting); if (val < GRIDSEED_MIN_FREQUENCY || val > GRIDSEED_MAX_FREQUENCY) { sprintf(replybuf, "invalid freq: '%s' valid range %d-%d", setting, GRIDSEED_MIN_FREQUENCY, GRIDSEED_MAX_FREQUENCY); return replybuf; } info->freq = val; set_freq_cmd(info, 0, 0, 0); gc3355_set_core_freq(gridseed); return NULL; } sprintf(replybuf, "Unknown option: %s", option); return replybuf; } static void gridseed_reinit(struct cgpu_info __maybe_unused *gridseed) { applog(LOG_DEBUG, "Entering %s", __FUNCTION__); } static void gridseed_shutdown(struct thr_info *thr) { applog(LOG_DEBUG, "Entering %s", __FUNCTION__); struct cgpu_info *gridseed = thr->cgpu; GRIDSEED_INFO *info = gridseed->device_data; applog(LOG_NOTICE, "%s%d: Shutting down", gridseed->drv->name, gridseed->device_id); pthread_join(info->th_packet, NULL); pthread_join(info->th_send, NULL); if (info->mode != MODE_SCRYPT_DUAL) pthread_join(info->th_read, NULL); cgsem_destroy(&info->psem); mutex_destroy(&info->qlock); mutex_destroy(&info->lock); if (info->sockltc != -1) { sockclose(info->sockltc); info->sockltc = -1; } if (info->mode != MODE_SCRYPT_DUAL) gc3355_send_cmds(gridseed, str_reset); } static void gridseed_hw_errors(struct thr_info __maybe_unused *thr) { applog(LOG_DEBUG, "Entering %s", __FUNCTION__); #if 0 struct cgpu_info *gridseed = thr->cgpu; GRIDSEED_INFO *info = gridseed->device_data; if (gridseed->hw_errors > 5) { gc3355_init(gridseed, info, true); gridseed->hw_errors = 0; applog(LOG_ERR, "HW error, do hardware reset"); } #endif } /* driver functions */ struct device_drv gridseed_drv = { .drv_id = DRIVER_gridseed, .dname = "gridseed", .name = "GSD", .drv_detect = gridseed_detect, .thread_prepare = gridseed_prepare, // called before miner thread is created .thread_init = gridseed_thread_init, // called at start of miner thread //.thread_enable = gridseed_thread_enable, //.hash_work // set in gridseed_prepare // called in miner thread, main loop .queue_full = gridseed_full, // sha256 only // called to determine if driver needs more work; applies only if hash_work = hash_queued_work //.scanwork // set in gridseed_prepare // called repeatedly from the loop in hash_work .flush_work = gridseed_flush_work, // called when new block detected on network */ .update_work = gridseed_update_work, .get_api_stats = gridseed_api_stats, .get_statline_before = gridseed_get_statline_before, .get_statline = gridseed_get_statline, .set_device = gridseed_set_device, .reinit_device = gridseed_reinit, // used by watchdog to restart hung drivers .thread_shutdown = gridseed_shutdown, // called last thing in miner thread, after hash_work has returned .hw_error = gridseed_hw_errors, // called from submit_nonce or inc_hw_errors };