driver-avalonmm.c 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196
  1. /*
  2. * Copyright 2014 Luke Dashjr
  3. *
  4. * This program is free software; you can redistribute it and/or modify it
  5. * under the terms of the GNU General Public License as published by the Free
  6. * Software Foundation; either version 3 of the License, or (at your option)
  7. * any later version. See COPYING for more details.
  8. */
  9. #include "config.h"
  10. #include <stdbool.h>
  11. #include <stdint.h>
  12. #include <stdlib.h>
  13. #include <string.h>
  14. #include <unistd.h>
  15. #include <utlist.h>
  16. #include "deviceapi.h"
  17. #include "logging.h"
  18. #include "lowlevel.h"
  19. #include "lowl-vcom.h"
  20. #include "util.h"
  21. #define AVALONMM_MAX_MODULES 4
  22. BFG_REGISTER_DRIVER(avalonmm_drv)
  23. #define AVALONMM_PKT_DATA_SIZE 0x20
  24. #define AVALONMM_PKT_SIZE (AVALONMM_PKT_DATA_SIZE + 7)
  25. enum avalonmm_cmd {
  26. AMC_DETECT = 0x0a,
  27. };
  28. enum avalonmm_reply {
  29. AMR_DETECT_ACK = 0x19,
  30. };
  31. static
  32. bool avalonmm_write_cmd(const int fd, const enum avalonmm_cmd cmd, const void *data, size_t datasz)
  33. {
  34. uint8_t packets = ((datasz + AVALONMM_PKT_DATA_SIZE - 1) / AVALONMM_PKT_DATA_SIZE) ?: 1;
  35. uint8_t pkt[AVALONMM_PKT_SIZE] = {'A', 'V', cmd, 1, packets};
  36. uint16_t crc;
  37. ssize_t r;
  38. while (true)
  39. {
  40. size_t copysz = AVALONMM_PKT_DATA_SIZE;
  41. if (datasz < copysz)
  42. {
  43. copysz = datasz;
  44. memset(&pkt[5 + copysz], '\0', AVALONMM_PKT_DATA_SIZE - copysz);
  45. }
  46. if (copysz)
  47. memcpy(&pkt[5], data, copysz);
  48. crc = crc16xmodem(&pkt[5], AVALONMM_PKT_DATA_SIZE);
  49. pk_u16be(pkt, 5 + AVALONMM_PKT_DATA_SIZE, crc);
  50. r = write(fd, pkt, sizeof(pkt));
  51. if (opt_dev_protocol)
  52. {
  53. char hex[(sizeof(pkt) * 2) + 1];
  54. bin2hex(hex, pkt, sizeof(pkt));
  55. applog(LOG_DEBUG, "DEVPROTO fd=%d SEND: %s => %d", fd, hex, (int)r);
  56. }
  57. if (sizeof(pkt) != r)
  58. return false;
  59. datasz -= copysz;
  60. if (!datasz)
  61. break;
  62. data += copysz;
  63. ++pkt[3];
  64. }
  65. return true;
  66. }
  67. static
  68. ssize_t avalonmm_read(const int fd, const int logprio, enum avalonmm_reply *out_reply, void * const bufp, size_t bufsz)
  69. {
  70. uint8_t *buf = bufp;
  71. uint8_t pkt[AVALONMM_PKT_SIZE];
  72. uint8_t packets = 0, got = 0;
  73. uint16_t good_crc, actual_crc;
  74. ssize_t r;
  75. while (true)
  76. {
  77. r = serial_read(fd, pkt, sizeof(pkt));
  78. if (opt_dev_protocol)
  79. {
  80. if (r >= 0)
  81. {
  82. char hex[(r * 2) + 1];
  83. bin2hex(hex, pkt, r);
  84. applog(LOG_DEBUG, "DEVPROTO fd=%d RECV: %s", fd, hex);
  85. }
  86. else
  87. applog(LOG_DEBUG, "DEVPROTO fd=%d RECV (%d)", fd, (int)r);
  88. }
  89. if (r != sizeof(pkt))
  90. applogr(-1, logprio, "%s: read failed", __func__);
  91. if (memcmp(pkt, "AV", 2))
  92. applogr(-1, logprio, "%s: bad header", __func__);
  93. good_crc = crc16xmodem(&pkt[5], AVALONMM_PKT_DATA_SIZE);
  94. actual_crc = upk_u16le(pkt, 5 + AVALONMM_PKT_DATA_SIZE);
  95. if (good_crc != actual_crc)
  96. applogr(-1, logprio, "%s: bad CRC (good=%04x actual=%04x)", __func__, good_crc, actual_crc);
  97. *out_reply = pkt[2];
  98. if (!got)
  99. {
  100. if (pkt[3] != 1)
  101. applogr(-1, logprio, "%s: first packet is not index 1", __func__);
  102. ++got;
  103. packets = pkt[4];
  104. }
  105. else
  106. {
  107. if (pkt[3] != ++got)
  108. applogr(-1, logprio, "%s: packet %d is not index %d", __func__, got, got);
  109. if (pkt[4] != packets)
  110. applogr(-1, logprio, "%s: packet %d total packet count is %d rather than original value of %d", __func__, got, pkt[4], packets);
  111. }
  112. if (bufsz)
  113. {
  114. if (likely(bufsz > AVALONMM_PKT_DATA_SIZE))
  115. {
  116. memcpy(buf, &pkt[5], AVALONMM_PKT_DATA_SIZE);
  117. bufsz -= AVALONMM_PKT_DATA_SIZE;
  118. buf += AVALONMM_PKT_DATA_SIZE;
  119. }
  120. else
  121. {
  122. memcpy(buf, &pkt[5], bufsz);
  123. bufsz = 0;
  124. }
  125. }
  126. if (got == packets)
  127. break;
  128. }
  129. return (((ssize_t)got) * AVALONMM_PKT_DATA_SIZE);
  130. }
  131. static
  132. bool avalonmm_detect_one(const char * const devpath)
  133. {
  134. uint8_t buf[AVALONMM_PKT_DATA_SIZE] = {0};
  135. enum avalonmm_reply reply;
  136. int total_modules = 0;
  137. const int fd = serial_open(devpath, 0, 1, true);
  138. if (fd == -1)
  139. applogr(false, LOG_DEBUG, "%s: Failed to open %s", __func__, devpath);
  140. for (int i = 0; i < AVALONMM_MAX_MODULES; ++i)
  141. {
  142. pk_u32be(buf, AVALONMM_PKT_DATA_SIZE - 4, i);
  143. avalonmm_write_cmd(fd, AMC_DETECT, buf, AVALONMM_PKT_DATA_SIZE);
  144. }
  145. while (avalonmm_read(fd, LOG_DEBUG, &reply, NULL, 0) > 0)
  146. {
  147. if (reply != AMR_DETECT_ACK)
  148. continue;
  149. int i = upk_u32be(buf, AVALONMM_PKT_DATA_SIZE - 4);
  150. applog(LOG_DEBUG, "%s: Confirmed module %d", __func__, i);
  151. ++total_modules;
  152. }
  153. serial_close(fd);
  154. if (!total_modules)
  155. return false;
  156. struct cgpu_info * const cgpu = malloc(sizeof(*cgpu));
  157. *cgpu = (struct cgpu_info){
  158. .drv = &avalonmm_drv,
  159. .device_path = strdup(devpath),
  160. .deven = DEV_ENABLED,
  161. .procs = total_modules,
  162. .threads = 1,
  163. };
  164. return add_cgpu(cgpu);
  165. }
  166. static
  167. bool avalonmm_lowl_probe(const struct lowlevel_device_info * const info)
  168. {
  169. return vcom_lowl_probe_wrapper(info, avalonmm_detect_one);
  170. }
  171. struct device_drv avalonmm_drv = {
  172. .dname = "avalonmm",
  173. .name = "AVM",
  174. .lowl_probe = avalonmm_lowl_probe,
  175. };