Page MenuHomeFreeBSD
Authored By
kjopek_gmail.com
Nov 11 2020, 10:09 AM
Size
10 KB
Referenced Files
None
Subscribers
None

rf_debug.c

/*
* This file contains typical path to check if we can communicate with BladeRF
* device: it loads fpga firmware (typically it breaks on RockPro64) and checks
* fpga firmware version.
*
* Based on libbladeRF initialization path. Wonder why firmware upload fails so
* spectacularly.
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <assert.h>
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <libusb.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#define BLADERF_SERIAL_LENGTH 33
#define BLADE_USB_CMD_QUERY_FPGA_STATUS 1
#define BLADE_USB_CMD_BEGIN_PROG 2
#define PERIPHERAL_EP_IN 0x82
#define PERIPHERAL_EP_OUT 0x02
#define PERIPHERAL_TIMEOUT_MS 500
#define CTRL_TIMEOUT_MS 1000
#define USB_IF_LEGACY_CONFIG 0
#define USB_IF_NULL 0
#define USB_IF_RF_LINK 1
#define USB_IF_SPI_FLASH 2
#define USB_IF_CONFIG 3
#define NIOS_PKT_LEGACY_MAGIC 'N'
#define NIOS_PKT_LEGACY_DEV_FPGA_VERSION_ID 12
#define NIOS_PKT_LEGACY_MODE_DEV_SHIFT 4
#define NIOS_PKT_LEGACY_DEV_CONFIG (0 << NIOS_PKT_LEGACY_MODE_DEV_SHIFT)
#define NIOS_PKT_LEGACY_MODE_DIR_MASK 0xC0
#define NIOS_PKT_LEGACY_MODE_DIR_SHIFT 6
#define NIOS_PKT_LEGACY_MODE_DIR_READ (2 << NIOS_PKT_LEGACY_MODE_DIR_SHIFT)
#define NIOS_PKT_LEGACY_MODE_DIR_WRITE (1 << NIOS_PKT_LEGACY_MODE_DIR_SHIFT)
typedef enum {
USB_TARGET_DEVICE,
USB_TARGET_INTERFACE,
USB_TARGET_ENDPOINT,
USB_TARGET_OTHER
} usb_target;
typedef enum {
USB_REQUEST_STANDARD,
USB_REQUEST_CLASS,
USB_REQUEST_VENDOR
} usb_request;
typedef enum {
USB_DIR_HOST_TO_DEVICE = 0x00,
USB_DIR_DEVICE_TO_HOST = 0x80
} usb_direction;
struct uart_cmd {
uint8_t addr;
uint8_t data;
};
struct bladerf_version {
uint8_t major;
uint8_t minor;
uint16_t patch;
};
static void
err_usb(const char *err_msg, int status)
{
err(1, "%s: %s", err_msg, libusb_error_name(status));
}
static void
change_setting(libusb_device_handle *handle, uint8_t setting)
{
int status;
status = libusb_set_interface_alt_setting(handle, 0, setting);
if (status < 0)
err_usb("libusb_set_interface_alt_setting failed", status);
}
static void
print_buf(const char *msg, const unsigned char *buf, size_t len)
{
unsigned ii;
printf("%s: ", msg);
for (ii = 0; ii < len; ii++)
printf("%02hhx", buf[ii]);
printf("\n");
}
static inline uint8_t
bm_request_type(usb_target target_type, usb_request req_type,
usb_direction direction)
{
uint8_t ret = 0;
switch (target_type) {
case USB_TARGET_DEVICE:
ret |= LIBUSB_RECIPIENT_DEVICE;
break;
case USB_TARGET_INTERFACE:
ret |= LIBUSB_RECIPIENT_INTERFACE;
break;
case USB_TARGET_ENDPOINT:
ret |= LIBUSB_RECIPIENT_ENDPOINT;
break;
default:
ret |= LIBUSB_RECIPIENT_OTHER;
}
switch (req_type) {
case USB_REQUEST_STANDARD:
ret |= LIBUSB_REQUEST_TYPE_STANDARD;
break;
case USB_REQUEST_CLASS:
ret |= LIBUSB_REQUEST_TYPE_CLASS;
break;
case USB_REQUEST_VENDOR:
ret |= LIBUSB_REQUEST_TYPE_VENDOR;
break;
}
switch (direction) {
case USB_DIR_HOST_TO_DEVICE:
ret |= LIBUSB_ENDPOINT_OUT;
break;
case USB_DIR_DEVICE_TO_HOST:
ret |= LIBUSB_ENDPOINT_IN;
break;
}
return (ret);
}
/* Vendor command that gets/sets a 32-bit integer value */
static inline int
vendor_cmd_int(libusb_device_handle *dev, uint8_t cmd, usb_direction dir,
int32_t *val)
{
const uint8_t bm_req_type = bm_request_type(USB_TARGET_DEVICE,
USB_REQUEST_VENDOR, dir);
return (libusb_control_transfer(dev, bm_req_type, cmd, 0, 0,
(uint8_t *)val, sizeof(int32_t), CTRL_TIMEOUT_MS));
}
static int
lusb_bulk_transfer(libusb_device_handle *handle, uint8_t endpoint, void *buffer,
uint32_t buffer_len, uint32_t timeout_ms)
{
int status;
int n_transferred;
status = libusb_bulk_transfer(handle, endpoint, buffer, buffer_len,
&n_transferred, timeout_ms);
printf("Libusb status: %d\n", status);
if (status == 0 && ((uint32_t)n_transferred != buffer_len)) {
printf("Short bulk transfer: requested=%u, transferred=%u\n",
buffer_len, n_transferred);
status = EIO;
}
return (status);
}
/* Stolen from libbladerf. */
static int
nios_access(libusb_device_handle *dev, uint8_t peripheral, usb_direction dir,
struct uart_cmd *cmd, size_t len)
{
int status;
size_t i;
uint8_t buf[16] = { 0 };
const uint8_t pkt_mode_dir = (dir == USB_DIR_HOST_TO_DEVICE) ?
NIOS_PKT_LEGACY_MODE_DIR_WRITE : NIOS_PKT_LEGACY_MODE_DIR_READ;
assert(len <= ((sizeof(buf) - 2) / 2));
/* Populate the buffer for transfer, given address data pairs */
buf[0] = NIOS_PKT_LEGACY_MAGIC;
buf[1] = pkt_mode_dir | peripheral | (uint8_t)len;
for (i = 0; i < len; i++) {
buf[i * 2 + 2] = cmd[i].addr;
buf[i * 2 + 3] = cmd[i].data;
}
print_buf("NIOS II access request", buf, 16);
/* Send the command */
status = lusb_bulk_transfer(dev, PERIPHERAL_EP_OUT, buf, sizeof(buf),
PERIPHERAL_TIMEOUT_MS);
if (status != 0) {
printf("Failed to submit NIOS II request: %s\n",
libusb_error_name(status));
return (status);
}
/* Read back the ACK. The command data is only used for a read operation,
* and is thrown away otherwise */
status = lusb_bulk_transfer(dev, PERIPHERAL_EP_IN, buf, sizeof(buf),
PERIPHERAL_TIMEOUT_MS);
if (dir == NIOS_PKT_LEGACY_MODE_DIR_READ && status == 0) {
for (i = 0; i < len; i++) {
cmd[i].data = buf[i * 2 + 3];
}
}
if (status == 0) {
print_buf("NIOS II access response:\n", buf, 16);
} else {
printf("Failed to receive NIOS II response: %s\n",
libusb_error_name(status));
}
return (status);
}
/* Stolen from libbladerf. */
static int
nios_legacy_get_fpga_version(libusb_device_handle *dev, struct bladerf_version
*ver)
{
change_setting(dev, USB_IF_RF_LINK);
int i, status;
struct uart_cmd cmd;
for (i = 0; i < 4; i++) {
cmd.addr = NIOS_PKT_LEGACY_DEV_FPGA_VERSION_ID + i;
cmd.data = 0xff;
status = nios_access(dev, NIOS_PKT_LEGACY_DEV_CONFIG,
USB_DIR_DEVICE_TO_HOST, &cmd, 1);
if (status != 0) {
printf("Failed to read FPGA version[%d]: %s (%d)\n", i,
libusb_error_name(status), status);
return (status);
}
switch (i) {
case 0:
ver->major = cmd.data;
break;
case 1:
ver->minor = cmd.data;
break;
case 2:
ver->patch = cmd.data;
break;
case 3:
ver->patch |= (cmd.data << 8);
break;
default:
assert(!"Bug");
}
}
return (status);
}
static int
is_fpga_configured(libusb_device_handle *handle)
{
int result = -1;
int status;
/* This environment variable provides a means to force libbladeRF to not
* attempt to access the FPGA.
*
* This provides a workaround for the situation where a user did not
* remove an FPGA in SPI flash prior to flashing new firmware and
* updating libbladeRF. Specifically, this has proven to be a problem
* with pre-v0.0.1 FPGA images, as they do not provide version readback
* functionality.
*/
status = vendor_cmd_int(handle, BLADE_USB_CMD_QUERY_FPGA_STATUS,
USB_DIR_DEVICE_TO_HOST, &result);
if (status < 0)
return (status);
else if (result == 0 || result == 1)
return (result);
assert(!"BUG");
}
static int
begin_fpga_programming(libusb_device_handle *handle)
{
int result;
int status = vendor_cmd_int(handle, BLADE_USB_CMD_BEGIN_PROG,
USB_DIR_DEVICE_TO_HOST, &result);
if (status != 0) {
return (status);
} else if (result != 0) {
printf("Startg fpga programming, result = %d\n", result);
return (LIBUSB_ERROR_OTHER);
} else {
return (0);
}
}
static int
load_fpga(libusb_device_handle *handle, const uint8_t *image, size_t image_size)
{
unsigned int wait_count;
const unsigned int timeout_ms = (2 * CTRL_TIMEOUT_MS);
int status;
/* Switch to the FPGA configuration interface */
change_setting(handle, USB_IF_CONFIG);
/* Begin programming */
status = begin_fpga_programming(handle);
if (status < 0) {
printf("Failed to initiate FPGA programming: %s (%d)\n",
libusb_error_name(status), status);
return (status);
}
/* Send the file down */
assert(image_size <= UINT32_MAX);
status = lusb_bulk_transfer(handle, PERIPHERAL_EP_OUT, (void *)image,
(uint32_t)image_size, timeout_ms);
if (status < 0) {
printf("Failed to write FPGA bitstream to FPGA: %s (%d)\n",
libusb_error_name(status), status);
return (status);
}
/* Poll FPGA status to determine if programming was a success */
wait_count = 10;
status = 0;
while (wait_count > 0 && status == 0) {
status = is_fpga_configured(handle);
if (status == 1) {
break;
}
usleep(200000);
wait_count--;
}
/* Failed to determine if FPGA is loaded */
if (status < 0) {
printf("Failed to determine if FPGA is loaded: %s (%d)\n",
libusb_error_name(status), status);
return (status);
} else if (wait_count == 0 && status != 0) {
printf("Timeout while waiting for FPGA configuration status\n");
return (LIBUSB_ERROR_TIMEOUT);
}
return (0);
}
static void
usage(void)
{
printf("test_bladerf <path to firmware>\n");
}
static int
load_firmware(libusb_device_handle *handle, const char *path)
{
struct stat stat;
uint8_t *image;
int fd;
fd = open(path, O_RDONLY);
if (fd < 0)
err(1, "open(2) failed");
if (fstat(fd, &stat) != 0)
err(1, "stat(2) failed");
image = calloc(1, stat.st_size);
if (image == NULL)
err(1, "calloc(2) failed");
if (read(fd, image, stat.st_size) != stat.st_size)
err(1, "read(2) failed");
close(fd);
if (load_fpga(handle, image, stat.st_size) != 0)
err(1, "could not load firmware");
free(image);
return (0);
}
int
main(int argc, char **argv)
{
libusb_device *dev = NULL;
libusb_device_handle *handle = NULL;
struct libusb_device_descriptor desc;
struct bladerf_version ver;
char serial[BLADERF_SERIAL_LENGTH];
int status;
if (argc != 2) {
usage();
return (1);
}
status = libusb_init(NULL);
if (status < 0)
err_usb("libusb_init failed", status);
libusb_set_debug(NULL, LIBUSB_DEBUG_FUNCTION);
handle = libusb_open_device_with_vid_pid(NULL, 0x2cf0, 0x5250);
if (handle == NULL)
err(1, "libusb_open_device_with_vid_pid(2) failed");
dev = libusb_get_device(handle);
if (dev == NULL)
err(1, "libusb_get_device failed");
status = libusb_claim_interface(handle, 0);
if (status < 0)
err_usb("libusb_claim_interface failed", status);
status = libusb_get_device_descriptor(dev, &desc);
if (status != 0)
err_usb("libusb_get_device_descriptor failed", status);
status = libusb_get_string_descriptor_ascii(handle, desc.iSerialNumber,
(unsigned char *)serial, BLADERF_SERIAL_LENGTH);
if (status < 0)
err_usb("libusb_get_string_descriptor_ascii failed", status);
serial[BLADERF_SERIAL_LENGTH-1] = '\0';
printf("Serial: %s\n", serial);
load_firmware(handle, argv[1]);
change_setting(handle, USB_IF_CONFIG);
memset(&ver, 0, sizeof(ver));
nios_legacy_get_fpga_version(handle, &ver);
printf("Version: %hhu.%hhu.%hu\n", ver.major, ver.minor, ver.patch);
/* Configure known state before leave. */
change_setting(handle, USB_IF_NULL);
libusb_close(handle);
libusb_exit(NULL);
return (0);
}

File Metadata

Mime Type
text/x-c
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
3082887
Default Alt Text
rf_debug.c (10 KB)

Event Timeline