/* transfer len bytes of data to the spi device */ int32_t ch341SpiStream(uint8_t *out, uint8_t *in, uint32_t len) { uint8_t inBuf[CH341_PACKET_LENGTH], outBuf[CH341_PACKET_LENGTH], *inPtr, *outPtr; int32_t ret, packetLen; bool done; if (devHandle == NULL) return -1; ch341SpiCs(outBuf, true); ret = usbTransfer(__func__, BULK_WRITE_ENDPOINT, outBuf, 4); if (ret < 0) return -1; inPtr = in; do { done=true; packetLen=len+1; // STREAM COMMAND + data length if (packetLen>CH341_PACKET_LENGTH) { packetLen=CH341_PACKET_LENGTH; done=false; } outPtr = outBuf; *outPtr++ = CH341A_CMD_SPI_STREAM; for (int i = 0; i < packetLen-1; ++i) *outPtr++ = swapByte(*out++); ret = usbTransfer(__func__, BULK_WRITE_ENDPOINT, outBuf, packetLen); if (ret < 0) return -1; ret = usbTransfer(__func__, BULK_READ_ENDPOINT, inBuf, packetLen-1); if (ret < 0) return -1; len -= ret; for (int i = 0; i < ret; ++i) // swap the buffer *inPtr++ = swapByte(inBuf[i]); } while (!done); ch341SpiCs(outBuf, false); ret = usbTransfer(__func__, BULK_WRITE_ENDPOINT, outBuf, 3); if (ret < 0) return -1; return 0; }
/* 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; }
/* read the content of SPI device to buf, make sure the buf is big enough before call */ int32_t ch341SpiRead(uint8_t *buf, uint32_t add, uint32_t len) { uint8_t out[CH341_MAX_PACKET_LEN]; uint8_t in[CH341_PACKET_LENGTH]; if (devHandle == NULL) return -1; /* what subtracted is: 1. first cs package, 2. leading command for every other packages, * 3. second package contains read flash command and 3 bytes address */ const uint32_t max_payload = CH341_MAX_PACKET_LEN - CH341_PACKET_LENGTH - CH341_MAX_PACKETS + 1 - 4; uint32_t tmp, pkg_len, pkg_count; struct libusb_transfer *xferBulkIn, *xferBulkOut; uint32_t idx = 0; uint32_t ret = 0; int32_t old_counter; struct timeval tv = {0, 100}; memset(out, 0xff, CH341_MAX_PACKET_LEN); for (int i = 1; i < CH341_MAX_PACKETS; ++i) // fill CH341A_CMD_SPI_STREAM for every packet out[i * CH341_PACKET_LENGTH] = CH341A_CMD_SPI_STREAM; memset(in, 0x00, CH341_PACKET_LENGTH); xferBulkIn = libusb_alloc_transfer(0); xferBulkOut = libusb_alloc_transfer(0); while (len > 0) { ch341SpiCs(out, true); idx = CH341_PACKET_LENGTH + 1; out[idx++] = 0xC0; // byte swapped command for Flash Read tmp = add; for (int i = 0; i < 3; ++i) { // starting address of next read out[idx++] = swapByte((tmp >> 16) & 0xFF); tmp <<= 8; } if (len > max_payload) { pkg_len = CH341_MAX_PACKET_LEN; pkg_count = CH341_MAX_PACKETS - 1; len -= max_payload; add += max_payload; } else { pkg_count = (len + 4) / (CH341_PACKET_LENGTH - 1); if ((len + 4) % (CH341_PACKET_LENGTH - 1)) pkg_count ++; pkg_len = (pkg_count) * CH341_PACKET_LENGTH + ((len + 4) % (CH341_PACKET_LENGTH - 1)) + 1; len = 0; } bulkin_count = 0; libusb_fill_bulk_transfer(xferBulkIn, devHandle, BULK_READ_ENDPOINT, in, CH341_PACKET_LENGTH, cbBulkIn, buf, DEFAULT_TIMEOUT); buf += max_payload; // advance user's pointer libusb_submit_transfer(xferBulkIn); libusb_fill_bulk_transfer(xferBulkOut, devHandle, BULK_WRITE_ENDPOINT, out, pkg_len, cbBulkOut, NULL, DEFAULT_TIMEOUT); libusb_submit_transfer(xferBulkOut); old_counter = bulkin_count; while (bulkin_count < pkg_count) { libusb_handle_events_timeout(NULL, &tv); if (bulkin_count == -1) { // encountered error len = 0; 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; } } ch341SpiCs(out, false); ret = usbTransfer(__func__, BULK_WRITE_ENDPOINT, out, 3); if (ret < 0) break; if (force_stop == 1) { // user hit ctrl+C force_stop = 0; if (len > 0) fprintf(stderr, "User hit Ctrl+C, reading unfinished.\n"); break; } } libusb_free_transfer(xferBulkIn); libusb_free_transfer(xferBulkOut); return ret; }