2017-01-17 00:58:59 +08:00
|
|
|
#include "usb_if.hpp"
|
|
|
|
#include "ftd3xx/ftd3xx.h"
|
|
|
|
#include <algorithm>
|
|
|
|
#include <atomic>
|
|
|
|
#include <chrono>
|
|
|
|
#include <csignal>
|
|
|
|
#include <cstring>
|
|
|
|
#include <iomanip>
|
|
|
|
#include <iostream>
|
|
|
|
#include <stdexcept>
|
|
|
|
#include <thread>
|
|
|
|
#include <utility>
|
|
|
|
#include <vector>
|
|
|
|
using namespace std;
|
|
|
|
|
|
|
|
namespace RFTool {
|
|
|
|
|
2017-04-15 17:54:50 +08:00
|
|
|
static const bool debug_enable = false;
|
|
|
|
|
2017-01-17 00:58:59 +08:00
|
|
|
void USBInterface::flush() { FT_FlushPipe(handle, 0); }
|
|
|
|
|
|
|
|
// Build a 32-byte command from command type and payload
|
|
|
|
void USBInterface::build_command(uint8_t cmd_type,
|
|
|
|
const vector<uint8_t> &payload,
|
|
|
|
uint8_t *buffer) {
|
2017-04-15 17:54:50 +08:00
|
|
|
fill(buffer, buffer + command_len, 0);
|
2017-01-17 00:58:59 +08:00
|
|
|
// Magic
|
|
|
|
buffer[0] = 0x43;
|
|
|
|
buffer[1] = 0x4d;
|
|
|
|
buffer[2] = 0x4e;
|
|
|
|
buffer[3] = 0x44;
|
|
|
|
// Cmd type
|
|
|
|
buffer[4] = cmd_type;
|
|
|
|
for (size_t i = 0; i < min(payload.size(), size_t(command_len - 5)); i++) {
|
|
|
|
buffer[i + 5] = payload[i];
|
|
|
|
}
|
|
|
|
swap_endian(buffer, command_len);
|
|
|
|
};
|
|
|
|
|
|
|
|
bool USBInterface::exec_command(uint8_t *command, bool get_response,
|
|
|
|
uint8_t *response) {
|
|
|
|
flush();
|
|
|
|
FT_STATUS err;
|
|
|
|
ULONG count;
|
2017-10-03 21:30:08 +08:00
|
|
|
if (debug_enable)
|
2017-04-15 17:54:50 +08:00
|
|
|
cout << "---------------------" << endl;
|
|
|
|
print_command(command);
|
2017-01-17 00:58:59 +08:00
|
|
|
err = FT_WritePipeEx(handle, 0, command, command_len, &count, timeout);
|
|
|
|
if (err != FT_OK) {
|
|
|
|
cerr << "write cmd returned error code " << int(err) << endl;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (get_response) {
|
|
|
|
err = FT_ReadPipeEx(handle, 0, response, response_len, &count, timeout);
|
|
|
|
if (err != FT_OK) {
|
|
|
|
cerr << "read resp returned error code " << int(err) << endl;
|
|
|
|
return false;
|
|
|
|
}
|
2017-04-15 17:54:50 +08:00
|
|
|
print_response(response);
|
2017-01-17 00:58:59 +08:00
|
|
|
}
|
|
|
|
return true;
|
|
|
|
};
|
|
|
|
|
|
|
|
// Print the response to cout
|
|
|
|
void USBInterface::print_response(uint8_t *resp) {
|
2017-04-15 17:54:50 +08:00
|
|
|
if (debug_enable) {
|
|
|
|
const int line_width = 16;
|
|
|
|
// Save and restore cout state
|
|
|
|
ios::fmtflags f(cout.flags());
|
|
|
|
cout << "Response:" << endl;
|
|
|
|
for (int i = 0; i < response_len; i++) {
|
|
|
|
cout << hex << setw(2) << setfill('0') << int(resp[i]);
|
|
|
|
if ((i % line_width) == (line_width - 1)) {
|
|
|
|
cout << endl;
|
|
|
|
} else {
|
|
|
|
cout << " ";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
cout << endl;
|
|
|
|
cout.flags(f);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void USBInterface::print_command(uint8_t *resp) {
|
|
|
|
if (debug_enable) {
|
|
|
|
const int line_width = 16;
|
|
|
|
// Save and restore cout state
|
|
|
|
ios::fmtflags f(cout.flags());
|
|
|
|
cout << "Command:" << endl;
|
|
|
|
for (int i = 0; i < command_len; i++) {
|
|
|
|
cout << hex << setw(2) << setfill('0') << int(resp[i]);
|
|
|
|
if ((i % line_width) == (line_width - 1)) {
|
|
|
|
cout << endl;
|
|
|
|
} else {
|
|
|
|
cout << " ";
|
|
|
|
}
|
2017-01-17 00:58:59 +08:00
|
|
|
}
|
2017-04-15 17:54:50 +08:00
|
|
|
cout << endl;
|
|
|
|
cout.flags(f);
|
2017-01-17 00:58:59 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool USBInterface::spi_transfer(bool is_read, int len, uint16_t addr,
|
|
|
|
uint8_t *buf) {
|
|
|
|
uint8_t cmd[command_len], resp[response_len];
|
|
|
|
for (int i = 0; i < command_len; i++)
|
|
|
|
cmd[i] = 0;
|
|
|
|
uint16_t hdr = 0;
|
|
|
|
hdr |= is_read ? 0x0000 : 0x8000;
|
|
|
|
hdr |= ((len - 1) & 0x07) << 12;
|
|
|
|
hdr |= addr & 0x3FF;
|
|
|
|
vector<uint8_t> payload;
|
|
|
|
payload.push_back(uint8_t(len + 2)); // 2 byte hdr + user data
|
|
|
|
payload.push_back((hdr >> 8) & 0xFF);
|
|
|
|
payload.push_back(hdr & 0xFF);
|
|
|
|
if (!is_read)
|
|
|
|
for (int i = 0; i < len; i++)
|
|
|
|
payload.push_back(buf[i]);
|
|
|
|
build_command(0x10, payload, cmd);
|
|
|
|
bool res = exec_command(cmd, true, resp);
|
|
|
|
swap_endian(resp, response_len);
|
|
|
|
if (res)
|
|
|
|
print_response(resp);
|
|
|
|
if (is_read)
|
|
|
|
for (int i = 0; i < len; i++)
|
|
|
|
buf[i] = resp[8 + i];
|
|
|
|
return res;
|
|
|
|
};
|
|
|
|
|
|
|
|
bool USBInterface::set_fpga_ctrl(uint8_t ctrl_val) {
|
|
|
|
uint8_t cmd[command_len], resp[response_len];
|
|
|
|
build_command(0x20, vector<uint8_t>{ctrl_val}, cmd);
|
|
|
|
bool res = exec_command(cmd, true, resp);
|
|
|
|
if (res)
|
|
|
|
print_response(resp);
|
|
|
|
return res;
|
|
|
|
};
|
|
|
|
|
|
|
|
bool USBInterface::get_rx_packet(uint8_t *buffer) {
|
|
|
|
ULONG count;
|
|
|
|
FT_STATUS err =
|
|
|
|
FT_ReadPipeEx(handle, 0, buffer, rx_packet_len, &count, timeout);
|
|
|
|
if (err != FT_OK) {
|
|
|
|
cerr << "read rx returned error code " << int(err) << endl;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
// cout << count << endl;
|
|
|
|
// swap_endian(buffer, rx_packet_len);
|
|
|
|
return true;
|
|
|
|
};
|
|
|
|
|
|
|
|
void USBInterface::get_version(void) {
|
|
|
|
DWORD dwVersion;
|
|
|
|
|
|
|
|
FT_GetDriverVersion(NULL, &dwVersion);
|
|
|
|
printf("Driver version:%d.%d.%d.%d\r\n", dwVersion >> 24,
|
|
|
|
(uint8_t)(dwVersion >> 16), (uint8_t)(dwVersion >> 8),
|
|
|
|
dwVersion & 0xFF);
|
|
|
|
|
|
|
|
FT_GetLibraryVersion(&dwVersion);
|
|
|
|
printf("Library version:%d.%d.%d\r\n", dwVersion >> 24,
|
|
|
|
(uint8_t)(dwVersion >> 16), dwVersion & 0xFFFF);
|
|
|
|
}
|
|
|
|
|
|
|
|
void USBInterface::get_vid_pid() {
|
|
|
|
WORD vid, pid;
|
|
|
|
|
|
|
|
if (FT_OK != FT_GetVIDPID(handle, &vid, &pid))
|
|
|
|
return;
|
|
|
|
printf("VID:%04X PID:%04X\r\n", vid, pid);
|
|
|
|
}
|
|
|
|
|
|
|
|
void USBInterface::turn_off_all_pipes() {
|
|
|
|
FT_TRANSFER_CONF conf;
|
|
|
|
|
|
|
|
memset(&conf, 0, sizeof(FT_TRANSFER_CONF));
|
|
|
|
conf.wStructSize = sizeof(FT_TRANSFER_CONF);
|
|
|
|
conf.pipe[FT_PIPE_DIR_IN].fPipeNotUsed = true;
|
|
|
|
conf.pipe[FT_PIPE_DIR_OUT].fPipeNotUsed = true;
|
|
|
|
for (DWORD i = 0; i < 4; i++)
|
|
|
|
FT_SetTransferParams(&conf, i);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool USBInterface::get_device_lists(int timeout_ms) {
|
|
|
|
DWORD count;
|
|
|
|
FT_DEVICE_LIST_INFO_NODE nodes[16];
|
|
|
|
|
|
|
|
chrono::steady_clock::time_point const timeout =
|
|
|
|
chrono::steady_clock::now() + chrono::milliseconds(timeout_ms);
|
|
|
|
|
|
|
|
do {
|
|
|
|
if (FT_OK == FT_CreateDeviceInfoList(&count))
|
|
|
|
break;
|
|
|
|
this_thread::sleep_for(chrono::microseconds(10));
|
|
|
|
} while (chrono::steady_clock::now() < timeout);
|
|
|
|
printf("Total %u device(s)\r\n", count);
|
|
|
|
if (!count)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (FT_OK != FT_GetDeviceInfoList(nodes, &count))
|
|
|
|
return false;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool USBInterface::set_ft600_channel_config(FT_60XCONFIGURATION *cfg,
|
|
|
|
CONFIGURATION_FIFO_CLK clock,
|
|
|
|
bool is_600_mode) {
|
|
|
|
bool needs_update = false;
|
|
|
|
bool current_is_600mode;
|
|
|
|
|
|
|
|
if (cfg->OptionalFeatureSupport &
|
|
|
|
CONFIGURATION_OPTIONAL_FEATURE_ENABLENOTIFICATIONMESSAGE_INCHALL) {
|
|
|
|
/* Notification in D3XX for Linux is implemented at OS level
|
|
|
|
* Turn off notification feature in firmware */
|
|
|
|
cfg->OptionalFeatureSupport &=
|
|
|
|
~CONFIGURATION_OPTIONAL_FEATURE_ENABLENOTIFICATIONMESSAGE_INCHALL;
|
|
|
|
needs_update = true;
|
|
|
|
printf("Turn off firmware notification feature\r\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!(cfg->OptionalFeatureSupport &
|
|
|
|
CONFIGURATION_OPTIONAL_FEATURE_DISABLECANCELSESSIONUNDERRUN)) {
|
|
|
|
/* Turn off feature not supported by D3XX for Linux */
|
|
|
|
cfg->OptionalFeatureSupport |=
|
|
|
|
CONFIGURATION_OPTIONAL_FEATURE_DISABLECANCELSESSIONUNDERRUN;
|
|
|
|
needs_update = true;
|
|
|
|
printf("disable cancel session on FIFO underrun 0x%X\r\n",
|
|
|
|
cfg->OptionalFeatureSupport);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (cfg->FIFOClock != clock)
|
|
|
|
needs_update = true;
|
|
|
|
|
|
|
|
if (cfg->FIFOMode == CONFIGURATION_FIFO_MODE_245) {
|
|
|
|
printf("FIFO is running at FT245 mode\r\n");
|
|
|
|
current_is_600mode = false;
|
|
|
|
} else if (cfg->FIFOMode == CONFIGURATION_FIFO_MODE_600) {
|
|
|
|
printf("FIFO is running at FT600 mode\r\n");
|
|
|
|
current_is_600mode = true;
|
|
|
|
} else {
|
|
|
|
printf("FIFO is running at unknown mode\r\n");
|
|
|
|
exit(-1);
|
|
|
|
}
|
|
|
|
|
|
|
|
UCHAR ch;
|
|
|
|
|
|
|
|
if (in_ch_cnt == 1 && out_ch_cnt == 0)
|
|
|
|
ch = CONFIGURATION_CHANNEL_CONFIG_1_INPIPE;
|
|
|
|
else if (in_ch_cnt == 0 && out_ch_cnt == 1)
|
|
|
|
ch = CONFIGURATION_CHANNEL_CONFIG_1_OUTPIPE;
|
|
|
|
else {
|
|
|
|
UCHAR total = in_ch_cnt < out_ch_cnt ? out_ch_cnt : in_ch_cnt;
|
|
|
|
|
|
|
|
if (total == 4)
|
|
|
|
ch = CONFIGURATION_CHANNEL_CONFIG_4;
|
|
|
|
else if (total == 2)
|
|
|
|
ch = CONFIGURATION_CHANNEL_CONFIG_2;
|
|
|
|
else
|
|
|
|
ch = CONFIGURATION_CHANNEL_CONFIG_1;
|
|
|
|
|
|
|
|
if (cfg->FIFOMode == CONFIGURATION_FIFO_MODE_245 && total > 1) {
|
|
|
|
printf("245 mode only support single channel\r\n");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (cfg->ChannelConfig == ch && current_is_600mode == is_600_mode &&
|
|
|
|
!needs_update)
|
|
|
|
return false;
|
|
|
|
cfg->ChannelConfig = ch;
|
|
|
|
cfg->FIFOClock = clock;
|
|
|
|
cfg->FIFOMode =
|
|
|
|
is_600_mode ? CONFIGURATION_FIFO_MODE_600 : CONFIGURATION_FIFO_MODE_245;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool USBInterface::set_channel_config(bool is_600_mode,
|
|
|
|
CONFIGURATION_FIFO_CLK clock) {
|
|
|
|
DWORD dwType;
|
|
|
|
|
|
|
|
/* Must turn off all pipes before changing chip configuration */
|
|
|
|
turn_off_all_pipes();
|
|
|
|
|
|
|
|
FT_GetDeviceInfoDetail(0, NULL, &dwType, NULL, NULL, NULL, NULL, &handle);
|
|
|
|
if (!handle)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
get_vid_pid();
|
|
|
|
|
|
|
|
union {
|
|
|
|
FT_60XCONFIGURATION ft600;
|
|
|
|
} cfg;
|
|
|
|
if (FT_OK != FT_GetChipConfiguration(handle, &cfg)) {
|
|
|
|
printf("Failed to get chip conf\r\n");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool needs_update;
|
|
|
|
needs_update = set_ft600_channel_config(&cfg.ft600, clock, is_600_mode);
|
|
|
|
if (needs_update) {
|
|
|
|
if (FT_OK != FT_SetChipConfiguration(handle, &cfg))
|
|
|
|
printf("Failed to set chip conf\r\n");
|
|
|
|
else {
|
|
|
|
printf("Configuration changed\r\n");
|
|
|
|
this_thread::sleep_for(chrono::seconds(1));
|
|
|
|
get_device_lists(6000);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (dwType == FT_DEVICE_600 || dwType == FT_DEVICE_601) {
|
|
|
|
bool rev_a_chip;
|
|
|
|
DWORD dwVersion;
|
|
|
|
|
|
|
|
FT_GetFirmwareVersion(handle, &dwVersion);
|
|
|
|
rev_a_chip = dwVersion <= 0x105;
|
|
|
|
|
|
|
|
FT_Close(handle);
|
|
|
|
return rev_a_chip;
|
|
|
|
}
|
|
|
|
|
|
|
|
FT_Close(handle);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
void USBInterface::turn_off_thread_safe() {
|
|
|
|
FT_TRANSFER_CONF conf;
|
|
|
|
|
|
|
|
memset(&conf, 0, sizeof(FT_TRANSFER_CONF));
|
|
|
|
conf.wStructSize = sizeof(FT_TRANSFER_CONF);
|
|
|
|
conf.pipe[FT_PIPE_DIR_IN].fNonThreadSafeTransfer = true;
|
|
|
|
conf.pipe[FT_PIPE_DIR_OUT].fNonThreadSafeTransfer = true;
|
2017-10-03 21:30:08 +08:00
|
|
|
conf.pipe[FT_PIPE_DIR_IN].dwURBBufferSize = 512UL * 1024UL;
|
|
|
|
conf.pipe[FT_PIPE_DIR_OUT].dwURBBufferSize = 512UL * 1024UL;
|
|
|
|
|
2017-01-17 00:58:59 +08:00
|
|
|
for (DWORD i = 0; i < 4; i++)
|
|
|
|
FT_SetTransferParams(&conf, i);
|
|
|
|
}
|
|
|
|
|
|
|
|
void USBInterface::get_queue_status() {
|
|
|
|
for (uint8_t channel = 0; channel < out_ch_cnt; channel++) {
|
|
|
|
DWORD dwBufferred;
|
|
|
|
|
|
|
|
if (FT_OK != FT_GetUnsentBuffer(handle, channel, NULL, &dwBufferred)) {
|
|
|
|
printf("Failed to get unsent buffer size\r\n");
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
unique_ptr<uint8_t[]> p(new uint8_t[dwBufferred]);
|
|
|
|
|
|
|
|
printf("CH%d OUT unsent buffer size in queue:%u\r\n", channel, dwBufferred);
|
|
|
|
if (FT_OK != FT_GetUnsentBuffer(handle, channel, p.get(), &dwBufferred)) {
|
|
|
|
printf("Failed to read unsent buffer size\r\n");
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for (uint8_t channel = 0; channel < in_ch_cnt; channel++) {
|
|
|
|
DWORD dwBufferred;
|
|
|
|
|
|
|
|
if (FT_OK != FT_GetReadQueueStatus(handle, channel, &dwBufferred))
|
|
|
|
continue;
|
|
|
|
printf("CH%d IN unread buffer size in queue:%u\r\n", channel, dwBufferred);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
USBInterface::USBInterface() {
|
|
|
|
get_version();
|
|
|
|
|
|
|
|
if (!get_device_lists(500))
|
|
|
|
throw runtime_error("failed to get device lists");
|
|
|
|
rev_a_chip = set_channel_config(fifo_600mode, CONFIGURATION_FIFO_CLK_100);
|
2017-04-15 17:54:50 +08:00
|
|
|
// turn_off_thread_safe(); // Check this?
|
2017-01-17 00:58:59 +08:00
|
|
|
FT_Create(0, FT_OPEN_BY_INDEX, &handle);
|
|
|
|
|
|
|
|
if (!handle) {
|
|
|
|
throw runtime_error("failed to create device");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
USBInterface::~USBInterface() {
|
|
|
|
if (rev_a_chip)
|
|
|
|
FT_ResetDevicePort(handle);
|
|
|
|
FT_Close(handle);
|
|
|
|
};
|
|
|
|
}
|