/* write buffer(*buf) to SPI flash */ int32_t ch341SpiWrite(uint8_t *buf, uint32_t add, uint32_t len) { uint8_t out[WRITE_PAYLOAD_LENGTH]; uint8_t in[CH341_PACKET_LENGTH]; uint32_t tmp, pkg_count; struct libusb_transfer *xferBulkIn, *xferBulkOut; uint32_t idx = 0; uint32_t ret; int32_t old_counter; struct timeval tv = {0, 100}; if (devHandle == NULL) return -1; memset(out, 0xff, WRITE_PAYLOAD_LENGTH); xferBulkIn = libusb_alloc_transfer(0); xferBulkOut = libusb_alloc_transfer(0); while (len > 0) { out[0] = 0x06; // Write enable ret = ch341SpiStream(out, in, 1); ch341SpiCs(out, true); idx = CH341_PACKET_LENGTH; out[idx++] = CH341A_CMD_SPI_STREAM; out[idx++] = 0x40; // byte swapped command for Flash Page Write tmp = add; for (int i = 0; i < 3; ++i) { // starting address of next write out[idx++] = swapByte((tmp >> 16) & 0xFF); tmp <<= 8; } tmp = 0; pkg_count = 1; while ((idx < WRITE_PAYLOAD_LENGTH) && (len > tmp)) { if (idx % CH341_PACKET_LENGTH == 0) { out[idx++] = CH341A_CMD_SPI_STREAM; pkg_count ++; } else { out[idx++] = swapByte(*buf++); tmp++; if (((add + tmp) & 0xFF) == 0) // cross page boundary break; } } len -= tmp; add += tmp; bulkin_count = 0; libusb_fill_bulk_transfer(xferBulkIn, devHandle, BULK_READ_ENDPOINT, in, CH341_PACKET_LENGTH, cbBulkIn, NULL, DEFAULT_TIMEOUT); libusb_submit_transfer(xferBulkIn); libusb_fill_bulk_transfer(xferBulkOut, devHandle, BULK_WRITE_ENDPOINT, out, idx, cbBulkOut, NULL, DEFAULT_TIMEOUT); libusb_submit_transfer(xferBulkOut); old_counter = bulkin_count; ret = 0; while (bulkin_count < pkg_count) { libusb_handle_events_timeout(NULL, &tv); if (bulkin_count == -1) { // encountered error ret = -1; break; } if (old_counter != bulkin_count) { // new package came if (bulkin_count != pkg_count) libusb_submit_transfer(xferBulkIn); // resubmit bulk in request old_counter = bulkin_count; } } if (ret < 0) break; ch341SpiCs(out, false); ret = usbTransfer(__func__, BULK_WRITE_ENDPOINT, out, 3); if (ret < 0) break; out[0] = 0x04; // Write disable ret = ch341SpiStream(out, in, 1); do { ret = ch341ReadStatus(); if (ret != 0) libusb_handle_events_timeout(NULL, &tv); } while(ret != 0); if (force_stop == 1) { // user hit ctrl+C force_stop = 0; if (len > 0) fprintf(stderr, "User hit Ctrl+C, writing unfinished.\n"); break; } } libusb_free_transfer(xferBulkIn); libusb_free_transfer(xferBulkOut); return ret; }
int main(int argc, char* argv[]) { int32_t ret; uint8_t *buf; FILE *fp; char *filename; int cap; int length = 0; char op = 0; uint32_t speed = CH341A_STM_I2C_20K; int8_t c; const char usage[] = "\nUsage:\n"\ " -h, --help display this message\n"\ " -v,--verbose print verbose info\n"\ " -i, --info read the chip ID info\n"\ " -e, --erase erase the entire chip\n"\ " -l, --length <bytes> manually set length\n"\ " -w, --write <filename> write chip with data from filename\n"\ " -r, --read <filename> read chip and save data to filename\n"\ " -t, --turbo increase the i2c bus speed (-tt to use much faster speed)\n"\ " -d, --double double the spi bus speed\n"; const struct option options[] = { {"help", no_argument, 0, 'h'}, {"erase", no_argument, 0, 'e'}, {"verbose", no_argument, 0, 'v'}, {"write", required_argument, 0, 'w'}, {"length", required_argument, 0, 'l'}, {"read", required_argument, 0, 'r'}, {"turbo", no_argument, 0, 't'}, {"double", no_argument, 0, 'd'}, {0, 0, 0, 0}}; int32_t optidx = 0; while ((c = getopt_long(argc, argv, "hiew:r:l:td:v", options, &optidx)) != -1){ switch (c) { case 'i': case 'e': if (!op) op = c; else op = 'x'; break; case 'v': verbose = 1; break; case 'w': case 'r': if (!op) { op = c; filename = (char*) malloc(strlen(optarg) + 1); strcpy(filename, optarg); } else op = 'x'; break; case 'l': length = atoi(optarg); break; case 't': if ((speed & 3) < 3) { speed++; } break; case 'd': speed |= CH341A_STM_SPI_DBL; break; default: printf("%s\n", usage); return 0; } } if (op == 0) { fprintf(stderr, "%s\n", usage); return 0; } if (op == 'x') { fprintf(stderr, "Conflicting options, only one option at a time.\n"); return -1; } ret = ch341Configure(CH341A_USB_VENDOR, CH341A_USB_PRODUCT); if (ret < 0) return -1; ret = ch341SetStream(speed); if (ret < 0) goto out; ret = ch341SpiCapacity(); if (ret < 0) goto out; cap = 1 << ret; printf("Chip capacity is %d\n", cap); if (length != 0){ cap = length; } if (op == 'i') goto out; if (op == 'e') { uint8_t timeout = 0; ret = ch341EraseChip(); if (ret < 0) goto out; do { sleep(1); ret = ch341ReadStatus(); if (ret < 0) goto out; printf("."); fflush(stdout); timeout++; if (timeout == 100) break; } while(ret != 0); if (timeout == 100) fprintf(stderr, "Chip erase timeout.\n"); else printf("Chip erase done!\n"); } if ((op == 'r') || (op == 'w')) { buf = (uint8_t *)malloc(cap); if (!buf) { fprintf(stderr, "Malloc failed for read buffer.\n"); goto out; } } if (op == 'r') { ret = ch341SpiRead(buf, 0, cap); if (ret < 0) goto out; fp = fopen(filename, "wb"); if (!fp) { fprintf(stderr, "Couldn't open file %s for writing.\n", filename); goto out; } fwrite(buf, 1, cap, fp); if (ferror(fp)) fprintf(stderr, "Error writing file [%s]\n", filename); fclose(fp); } if (op == 'w') { fp = fopen(filename, "rb"); if (!fp) { fprintf(stderr, "Couldn't open file %s for reading.\n", filename); goto out; } ret = fread(buf, 1, cap, fp); if (ferror(fp)) { fprintf(stderr, "Error reading file [%s]\n", filename); if (fp) fclose(fp); goto out; } fprintf(stderr, "File Size is [%d]\n", ret); fclose(fp); ret = ch341SpiWrite(buf, 0, ret); } out: ch341Release(); return 0; }