|
| 1 | +#ifdef WIN32 |
| 2 | +#include <windows.h> |
| 3 | +#endif |
| 4 | +#include <stdio.h> |
| 5 | +#include <stdlib.h> |
| 6 | +#include <string.h> |
| 7 | +#include <poll.h> |
| 8 | +#include <unistd.h> |
| 9 | +#include <sys/time.h> |
| 10 | +#include "hidapi.h" |
| 11 | +#include "uf2hid.h" |
| 12 | + |
| 13 | +#define FLASH_ROW_SIZE 4096 |
| 14 | + |
| 15 | +typedef struct { |
| 16 | + hid_device *dev; |
| 17 | + uint16_t size; |
| 18 | + uint8_t serial; |
| 19 | + uint16_t seqNo; |
| 20 | + uint16_t pageSize; |
| 21 | + uint8_t buf[FLASH_ROW_SIZE + 64]; |
| 22 | +} HID_Dev; |
| 23 | + |
| 24 | +uint64_t millis() { |
| 25 | + struct timeval tv; |
| 26 | + gettimeofday(&tv, 0); |
| 27 | + return tv.tv_sec * 1000 + tv.tv_usec / 1000; |
| 28 | +} |
| 29 | + |
| 30 | +void fatal(const char *msg) { |
| 31 | + fprintf(stderr, "Fatal error: %s\n", msg); |
| 32 | + exit(1); |
| 33 | +} |
| 34 | + |
| 35 | +void write16(uint8_t *ptr, uint16_t v) { |
| 36 | + ptr[0] = v; |
| 37 | + ptr[1] = v >> 8; |
| 38 | +} |
| 39 | + |
| 40 | +void write32(uint8_t *ptr, uint32_t v) { |
| 41 | + ptr[0] = v; |
| 42 | + ptr[1] = v >> 8; |
| 43 | + ptr[2] = v >> 16; |
| 44 | + ptr[3] = v >> 24; |
| 45 | +} |
| 46 | + |
| 47 | +uint32_t read32(uint8_t *ptr) { return ptr[0] | (ptr[1] << 8) | (ptr[2] << 16) | (ptr[3] << 24); } |
| 48 | + |
| 49 | +uint32_t read16(uint8_t *ptr) { return ptr[0] | (ptr[1] << 8); } |
| 50 | + |
| 51 | +int recv_hid(HID_Dev *pkt, int timeout) { |
| 52 | + uint8_t buf0[65]; |
| 53 | + uint8_t *buf; |
| 54 | + |
| 55 | + pkt->size = 0; |
| 56 | + memset(pkt->buf, 0, sizeof(pkt->buf)); |
| 57 | + |
| 58 | + for (;;) { |
| 59 | + int sz = hid_read_timeout(pkt->dev, buf0, 65, timeout); |
| 60 | + if (sz <= 0) { |
| 61 | + if (timeout < 0) |
| 62 | + fatal("read error"); |
| 63 | + return 0; |
| 64 | + } |
| 65 | + buf = buf0; |
| 66 | + if (!*buf) |
| 67 | + buf++; // skip report number if passed |
| 68 | + |
| 69 | + uint8_t tag = buf[0]; |
| 70 | + if (pkt->size && !(tag & 0x80)) |
| 71 | + fatal("invalid serial transfer"); |
| 72 | + uint32_t newsize = pkt->size + (tag & HF2_SIZE_MASK); |
| 73 | + if (newsize > sizeof(pkt->buf)) |
| 74 | + fatal("too large packet"); |
| 75 | + memcpy(pkt->buf + pkt->size, buf + 1, tag & HF2_SIZE_MASK); |
| 76 | + pkt->size = newsize; |
| 77 | + if (tag & 0x40) { |
| 78 | + pkt->serial = (tag & HF2_FLAG_MASK) == HF2_FLAG_SERIAL; |
| 79 | + return 1; |
| 80 | + } |
| 81 | + timeout = -1; // next read is blocking |
| 82 | + } |
| 83 | +} |
| 84 | + |
| 85 | +void send_hid(hid_device *dev, const void *data, int size) { |
| 86 | + uint8_t buf[65] = {0}; |
| 87 | + const uint8_t *ptr = data; |
| 88 | + |
| 89 | + for (;;) { |
| 90 | + int s; |
| 91 | + if (size <= 63) { |
| 92 | + s = size; |
| 93 | + buf[1] = HF2_FLAG_PKT_LAST | size; |
| 94 | + } else { |
| 95 | + s = 63; |
| 96 | + buf[1] = HF2_FLAG_PKT_BODY | 63; |
| 97 | + } |
| 98 | + memcpy(buf + 2, ptr, s); |
| 99 | + int sz = hid_write(dev, buf, 65); |
| 100 | + if (sz != 65) |
| 101 | + fatal("write error"); |
| 102 | + ptr += s; |
| 103 | + size -= s; |
| 104 | + if (!size) |
| 105 | + break; |
| 106 | + } |
| 107 | +} |
| 108 | + |
| 109 | +void talk_hid(HID_Dev *pkt, int cmd, const void *data, uint32_t len) { |
| 110 | + if (len >= sizeof(pkt->buf) - 4) |
| 111 | + fatal("buffer overflow"); |
| 112 | + if (data) |
| 113 | + memcpy(pkt->buf + 4, data, len); |
| 114 | + write16(pkt->buf, cmd); |
| 115 | + write16(pkt->buf + 2, ++pkt->seqNo); |
| 116 | + uint32_t saved = read32(pkt->buf); |
| 117 | + send_hid(pkt->dev, pkt->buf, 4 + len); |
| 118 | + recv_hid(pkt, -1); |
| 119 | + if (read32(pkt->buf) != saved) |
| 120 | + fatal("invalid sequence number"); |
| 121 | + if (read32(pkt->buf + 4) != 0) |
| 122 | + fatal("invalid status"); |
| 123 | +} |
| 124 | + |
| 125 | +uint8_t flashbuf[64 * 1024]; |
| 126 | + |
| 127 | +unsigned short add_crc(char ptr, unsigned short crc) { |
| 128 | + unsigned short cmpt; |
| 129 | + crc = crc ^ (int)ptr << 8; |
| 130 | + for (cmpt = 0; cmpt < 8; cmpt++) { |
| 131 | + if (crc & 0x8000) |
| 132 | + crc = crc << 1 ^ 0x1021; |
| 133 | + else |
| 134 | + crc = crc << 1; |
| 135 | + } |
| 136 | + return (crc & 0xFFFF); |
| 137 | +} |
| 138 | + |
| 139 | +void verify(HID_Dev *cmd, uint8_t *buf, int size, int offset) { |
| 140 | + int maxSize = (cmd->pageSize / 2 - 8) * cmd->pageSize; |
| 141 | + while (size > maxSize) { |
| 142 | + verify(cmd, buf, maxSize, offset); |
| 143 | + buf += maxSize; |
| 144 | + offset += maxSize; |
| 145 | + size -= maxSize; |
| 146 | + } |
| 147 | + int numpages = size / cmd->pageSize; |
| 148 | + write32(cmd->buf + 4, offset); |
| 149 | + write32(cmd->buf + 8, numpages); |
| 150 | + talk_hid(cmd, HF2_CMD_CHKSUM_PAGES, 0, 8); |
| 151 | + for (int i = 0; i < numpages; ++i) { |
| 152 | + int sum = read16(cmd->buf + 8 + i * 2); |
| 153 | + uint16_t crc = 0; |
| 154 | + for (int j = 0; j < cmd->pageSize; ++j) { |
| 155 | + crc = add_crc(buf[j], crc); |
| 156 | + } |
| 157 | + if (sum != crc) |
| 158 | + fatal("verification failed"); |
| 159 | + buf += cmd->pageSize; |
| 160 | + } |
| 161 | +} |
| 162 | + |
| 163 | +int stdinHasData() { |
| 164 | + struct pollfd fds; |
| 165 | + fds.fd = 0; |
| 166 | + fds.events = POLLIN; |
| 167 | + fds.revents = 0; |
| 168 | + return poll(&fds, 1, 0) > 0; |
| 169 | +} |
| 170 | + |
| 171 | +void serial(HID_Dev *cmd) { |
| 172 | + uint8_t buf[65]; |
| 173 | + for (;;) { |
| 174 | + while (stdinHasData()) { |
| 175 | + memset(buf, 0, 65); |
| 176 | + int sz = read(0, buf + 2, 63); |
| 177 | + if (sz > 0) { |
| 178 | + buf[1] = HF2_FLAG_SERIAL | sz; |
| 179 | + hid_write(cmd->dev, buf, 65); |
| 180 | + } |
| 181 | + } |
| 182 | + if (recv_hid(cmd, 10)) { |
| 183 | + if (cmd->serial) |
| 184 | + write(1, cmd->buf, cmd->size); |
| 185 | + } |
| 186 | + } |
| 187 | +} |
| 188 | + |
| 189 | +int main(int argc, char *argv[]) { |
| 190 | + int res; |
| 191 | + HID_Dev cmd = {0}; |
| 192 | + |
| 193 | + // Initialize the hidapi library |
| 194 | + res = hid_init(); |
| 195 | + |
| 196 | + struct hid_device_info *devs = hid_enumerate(0, 0); |
| 197 | + for (struct hid_device_info *p = devs; p; p = p->next) { |
| 198 | + if ((p->release_number & 0xff00) == 0x4200) { |
| 199 | + printf("DEV: %04x:%04x %s\n", p->vendor_id, p->product_id, p->path); |
| 200 | + cmd.dev = hid_open_path(p->path); |
| 201 | + } |
| 202 | + } |
| 203 | + hid_free_enumeration(devs); |
| 204 | + if (!cmd.dev) { |
| 205 | + printf("no devices\n"); |
| 206 | + return 0; |
| 207 | + } |
| 208 | + |
| 209 | + talk_hid(&cmd, HF2_CMD_INFO, 0, 0); |
| 210 | + printf("INFO: %s\n", cmd.buf + 8); |
| 211 | + |
| 212 | + serial(&cmd); |
| 213 | + |
| 214 | + talk_hid(&cmd, HF2_CMD_BININFO, 0, 0); |
| 215 | + if (read32(cmd.buf + 8) != HF2_MODE_BOOTLOADER) |
| 216 | + fatal("not bootloader"); |
| 217 | + |
| 218 | + cmd.pageSize = read32(cmd.buf + 12); |
| 219 | + printf("page size: %d\n", cmd.pageSize); |
| 220 | + |
| 221 | + srand(millis()); |
| 222 | + int i; |
| 223 | + for (i = 0; i < sizeof(flashbuf); ++i) |
| 224 | + flashbuf[i] = rand(); |
| 225 | + |
| 226 | + uint64_t start = millis(); |
| 227 | + |
| 228 | + for (i = 0; i < sizeof(flashbuf); i += cmd.pageSize) { |
| 229 | + write32(cmd.buf + 4, i + 0x2000); |
| 230 | + memcpy(cmd.buf + 8, flashbuf + i, cmd.pageSize); |
| 231 | + talk_hid(&cmd, HF2_CMD_WRITE_FLASH_PAGE, 0, cmd.pageSize + 4); |
| 232 | + } |
| 233 | + |
| 234 | + printf("time: %d\n", (int)(millis() - start)); |
| 235 | + start = millis(); |
| 236 | + |
| 237 | +#if 0 |
| 238 | + |
| 239 | + for (i = 0; i < sizeof(flashbuf); i += cmd.pageSize) { |
| 240 | + write32(cmd.buf + 4, i + 0x2000); |
| 241 | + talk_hid(&cmd, HF2_CMD_MEM_READ_PAGE, 0, 4); |
| 242 | + if (memcmp(cmd.buf + 8, flashbuf + i, cmd.pageSize)) { |
| 243 | + printf("%d,%d,%d != %d?\n", cmd.buf[8], cmd.buf[9], cmd.buf[10], flashbuf[i]); |
| 244 | + fatal("verification failed"); |
| 245 | + } |
| 246 | + } |
| 247 | +#else |
| 248 | + verify(&cmd, flashbuf, sizeof(flashbuf), 0x2000); |
| 249 | +#endif |
| 250 | + |
| 251 | + printf("verify time: %d\n", (int)(millis() - start)); |
| 252 | + |
| 253 | + // Finalize the hidapi library |
| 254 | + res = hid_exit(); |
| 255 | + |
| 256 | + return 0; |
| 257 | +} |
0 commit comments