/* * This file is part of the bladeRF project: * http://www.github.com/nuand/bladeRF * * Copyright (C) 2014-2017 Nuand LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include "rel_assert.h" #include "log.h" #include "minmax.h" #include "conversions.h" #include "usb.h" #include "board/board.h" #include "backend/backend.h" #include "backend/backend_config.h" #include "backend/usb/usb.h" #include "driver/fx3_fw.h" #include "streaming/async.h" #include "helpers/version.h" #include "bladeRF.h" #include "nios_pkt_formats.h" #include "nios_legacy_access.h" #include "nios_access.h" #if ENABLE_USB_DEV_RESET_ON_OPEN bool bladerf_usb_reset_device_on_open = true; #endif static const struct usb_driver *usb_driver_list[] = BLADERF_USB_BACKEND_LIST; /* FW declaration of fn table declared at the end of this file */ const struct backend_fns backend_fns_usb_legacy; /* Vendor command wrapper to gets a 32-bit integer and supplies a wIndex */ static inline int vendor_cmd_int_windex(struct bladerf *dev, uint8_t cmd, uint16_t windex, int32_t *val) { struct bladerf_usb *usb = dev->backend_data; return usb->fn->control_transfer(usb->driver, USB_TARGET_DEVICE, USB_REQUEST_VENDOR, USB_DIR_DEVICE_TO_HOST, cmd, 0, windex, val, sizeof(uint32_t), CTRL_TIMEOUT_MS); } /* Vendor command wrapper to get a 32-bit integer and supplies wValue */ static inline int vendor_cmd_int_wvalue(struct bladerf *dev, uint8_t cmd, uint16_t wvalue, int32_t *val) { struct bladerf_usb *usb = dev->backend_data; return usb->fn->control_transfer(usb->driver, USB_TARGET_DEVICE, USB_REQUEST_VENDOR, USB_DIR_DEVICE_TO_HOST, cmd, wvalue, 0, val, sizeof(uint32_t), CTRL_TIMEOUT_MS); } /* Vendor command that gets/sets a 32-bit integer value */ static inline int vendor_cmd_int(struct bladerf *dev, uint8_t cmd, usb_direction dir, int32_t *val) { struct bladerf_usb *usb = dev->backend_data; return usb->fn->control_transfer(usb->driver, USB_TARGET_DEVICE, USB_REQUEST_VENDOR, dir, cmd, 0, 0, val, sizeof(int32_t), CTRL_TIMEOUT_MS); } static inline int change_setting(struct bladerf *dev, uint8_t setting) { int status; struct bladerf_usb *usb = dev->backend_data; log_verbose("Changing to USB alt setting %u\n", setting); status = usb->fn->change_setting(usb->driver, setting); if (status != 0) { log_debug("Failed to change setting: %s\n", bladerf_strerror(status)); } return status; } static int usb_is_fpga_configured(struct bladerf *dev) { 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. */ if (getenv("BLADERF_FORCE_NO_FPGA_PRESENT")) { log_debug("Reporting no FPGA present - " "BLADERF_FORCE_NO_FPGA_PRESENT is set.\n"); return 0; } status = vendor_cmd_int(dev, 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; } else { log_debug("Unexpected result from FPGA status query: %d\n", result); return BLADERF_ERR_UNEXPECTED; } } static int usb_set_fpga_protocol(struct bladerf *dev, backend_fpga_protocol fpga_protocol) { if (fpga_protocol == BACKEND_FPGA_PROTOCOL_NIOSII_LEGACY) { dev->backend = &backend_fns_usb_legacy; } else if (fpga_protocol == BACKEND_FPGA_PROTOCOL_NIOSII) { dev->backend = &backend_fns_usb; } else { log_error("Unknown FPGA protocol: %d\n", fpga_protocol); return BLADERF_ERR_INVAL; } return 0; } static bladerf_fpga_source usb_get_fpga_source(struct bladerf *dev) { int result = -1; int status; status = vendor_cmd_int(dev, BLADE_USB_CMD_QUERY_FPGA_SOURCE, USB_DIR_DEVICE_TO_HOST, &result); if (status < 0) { log_debug("%s: vendor_cmd_int returned %s\n", __FUNCTION__, bladerf_strerror(status)); return BLADERF_FPGA_SOURCE_UNKNOWN; } else if (0 == result || 1 == result || 2 == result) { return (bladerf_fpga_source)result; } else { log_debug("Unexpected result from FPGA source query: %d\n", result); return BLADERF_FPGA_SOURCE_UNKNOWN; } } /* After performing a flash operation, switch back to either RF_LINK or the * FPGA loader. */ static int restore_post_flash_setting(struct bladerf *dev) { int fpga_loaded = usb_is_fpga_configured(dev); int status; if (fpga_loaded < 0) { status = fpga_loaded; log_debug("Failed to determine if FPGA is loaded (%d)\n", fpga_loaded); } else if (fpga_loaded) { status = change_setting(dev, USB_IF_RF_LINK); } else { status = change_setting(dev, USB_IF_CONFIG); } if (status < 0) { log_debug("Failed to restore alt setting: %s\n", bladerf_strerror(status)); } return status; } static bool usb_matches(bladerf_backend backend) { return backend == BLADERF_BACKEND_ANY || backend == BLADERF_BACKEND_LINUX || backend == BLADERF_BACKEND_LIBUSB || backend == BLADERF_BACKEND_CYPRESS; } static int usb_probe(backend_probe_target probe_target, struct bladerf_devinfo_list *info_list) { int status; size_t i; for (i = status = 0; i < ARRAY_SIZE(usb_driver_list); i++) { status = usb_driver_list[i]->fn->probe(probe_target, info_list); } return status; } static void usb_close(struct bladerf *dev) { int status; struct bladerf_usb *usb = dev->backend_data; if (usb != NULL) { /* It seems we need to switch back to our NULL interface before closing, * or else our device doesn't close upon exit in OSX and then fails to * re-open cleanly */ status = usb->fn->change_setting(usb->driver, USB_IF_NULL); if (status != 0) { log_error("Failed to switch to NULL interface: %s\n", bladerf_strerror(status)); } usb->fn->close(usb->driver); free(usb); dev->backend_data = NULL; } } static int usb_is_fw_ready(struct bladerf *dev) { int status; int result; status = vendor_cmd_int(dev, BLADE_USB_CMD_QUERY_DEVICE_READY, USB_DIR_DEVICE_TO_HOST, &result); if (status < 0) { return status; } else if (result == 0 || result == 1) { return result; } else { log_debug("Unexpected result from firmware status query: %d\n", result); return BLADERF_ERR_UNEXPECTED; } } static int usb_get_handle(struct bladerf *dev, void **handle) { struct bladerf_usb *usb = dev->backend_data; int status; status = usb->fn->get_handle(usb->driver, handle); return status; } static int usb_get_fw_version(struct bladerf *dev, struct bladerf_version *version) { struct bladerf_usb *usb = dev->backend_data; int status; status = usb->fn->get_string_descriptor(usb->driver, BLADE_USB_STR_INDEX_FW_VER, (unsigned char *)version->describe, BLADERF_VERSION_STR_MAX); if (status == 0) { status = str2version(version->describe, version); } else { log_warning("Failed to retrieve firmware version. This may be due " "to an old firmware version that does not support " "this request. A firmware update via the bootloader is " "required.\n\n"); status = BLADERF_ERR_UPDATE_FW; } return status; } static int usb_get_fpga_version(struct bladerf *dev, struct bladerf_version *version) { int status; status = change_setting(dev, USB_IF_RF_LINK); if (status < 0) { return status; } /* Read and store FPGA version info. This is only possible after * we've entered RF link mode. * * The legacy mode is used here since we can't yet determine if * the FPGA is capable of using the newer packet formats. */ return nios_legacy_get_fpga_version(dev, version); } static int usb_open(struct bladerf *dev, struct bladerf_devinfo *info) { int status; size_t i; struct bladerf_usb *usb; usb = malloc(sizeof(*usb)); if (usb == NULL) { return BLADERF_ERR_MEM; } /* Try each matching usb driver */ for (i = 0; i < ARRAY_SIZE(usb_driver_list); i++) { if (info->backend == BLADERF_BACKEND_ANY || usb_driver_list[i]->id == info->backend) { usb->fn = usb_driver_list[i]->fn; status = usb->fn->open(&usb->driver, info, &dev->ident); if (status == 0) { break; } else if (status == BLADERF_ERR_NODEV) { continue; } else { free(usb); return status; } } } /* If no usb driver was found */ if (i == ARRAY_SIZE(usb_driver_list)) { free(usb); return BLADERF_ERR_NODEV; } /* Default to legacy-mode access until we determine the FPGA is * capable of handling newer request formats */ dev->backend = &backend_fns_usb_legacy; dev->backend_data = usb; /* Just out of paranoia, put the device into a known state */ status = change_setting(dev, USB_IF_NULL); if (status < 0) { log_debug("Failed to switch to USB_IF_NULL\n"); goto error; } error: if (status != 0) { usb_close(dev); } return status; } static int usb_get_vid_pid(struct bladerf *dev, uint16_t *vid, uint16_t *pid) { struct bladerf_usb *usb = dev->backend_data; return usb->fn->get_vid_pid(usb->driver, vid, pid); } static int usb_get_flash_id(struct bladerf *dev, uint8_t *mid, uint8_t *did) { int status; int result; status = vendor_cmd_int(dev, BLADE_USB_CMD_QUERY_FLASH_ID, USB_DIR_DEVICE_TO_HOST, &result); if (status < 0) { log_debug("Could not read flash manufacturer ID and/or device ID. %s.\n", bladerf_strerror(status)); } else { *did = result & 0xFF; *mid = (result >> 8) & 0xFF; } return status; } static int begin_fpga_programming(struct bladerf *dev) { int result; int status = vendor_cmd_int(dev, BLADE_USB_CMD_BEGIN_PROG, USB_DIR_DEVICE_TO_HOST, &result); if (status != 0) { return status; } else if (result != 0) { log_debug("Startg fpga programming, result = %d\n", result); return BLADERF_ERR_UNEXPECTED; } else { return 0; } } static int usb_load_fpga(struct bladerf *dev, const uint8_t *image, size_t image_size) { struct bladerf_usb *usb = dev->backend_data; unsigned int wait_count; const unsigned int timeout_ms = (3 * CTRL_TIMEOUT_MS); int status; /* Switch to the FPGA configuration interface */ status = change_setting(dev, USB_IF_CONFIG); if(status < 0) { log_debug("Failed to switch to FPGA config setting: %s\n", bladerf_strerror(status)); return status; } /* Begin programming */ status = begin_fpga_programming(dev); if (status < 0) { log_debug("Failed to initiate FPGA programming: %s\n", bladerf_strerror(status)); return status; } /* Send the file down */ assert(image_size <= UINT32_MAX); status = usb->fn->bulk_transfer(usb->driver, PERIPHERAL_EP_OUT, (void *)image, (uint32_t)image_size, timeout_ms); if (status < 0) { log_debug("Failed to write FPGA bitstream to FPGA: %s\n", bladerf_strerror(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 = usb_is_fpga_configured(dev); if (status == 1) { break; } usleep(200000); wait_count--; } /* Failed to determine if FPGA is loaded */ if (status < 0) { log_debug("Failed to determine if FPGA is loaded: %s\n", bladerf_strerror(status)); return status; } else if (wait_count == 0 && status != 0) { log_debug("Timeout while waiting for FPGA configuration status\n"); return BLADERF_ERR_TIMEOUT; } return 0; } static inline int perform_erase(struct bladerf *dev, uint16_t block) { int status, erase_ret; struct bladerf_usb *usb = dev->backend_data; status = usb->fn->control_transfer(usb->driver, USB_TARGET_DEVICE, USB_REQUEST_VENDOR, USB_DIR_DEVICE_TO_HOST, BLADE_USB_CMD_FLASH_ERASE, 0, block, &erase_ret, sizeof(erase_ret), CTRL_TIMEOUT_MS); return status; } static int usb_erase_flash_blocks(struct bladerf *dev, uint32_t eb, uint16_t count) { int status, restore_status; uint16_t i; status = change_setting(dev, USB_IF_SPI_FLASH); if (status != 0) { return status; } log_info("Erasing %u block%s starting at block %u\n", count, 1 == count ? "" : "s", eb); for (i = 0; i < count; i++) { log_info("Erasing block %u (%u%%)...%c", eb + i, (i + 1) == count ? 100 : 100 * i / count, (i + 1) == count ? '\n' : '\r'); status = perform_erase(dev, eb + i); if (status != 0) { log_debug("Failed to erase block %u: %s\n", eb + i, bladerf_strerror(status)); goto error; } } log_info("Done erasing %u block%s\n", count, 1 == count ? "" : "s"); error: restore_status = restore_post_flash_setting(dev); return status != 0 ? status : restore_status; } static inline int read_page(struct bladerf *dev, uint8_t read_operation, uint16_t page, uint8_t *buf) { struct bladerf_usb *usb = dev->backend_data; bladerf_dev_speed usb_speed; int status; int32_t op_status; uint16_t read_size; uint16_t offset; uint8_t request; if (usb->fn->get_speed(usb->driver, &usb_speed) != 0) { log_debug("Error getting USB speed in %s\n", __FUNCTION__); return BLADERF_ERR_UNEXPECTED; } if (usb_speed == BLADERF_DEVICE_SPEED_SUPER) { read_size = dev->flash_arch->psize_bytes; } else if (usb_speed == BLADERF_DEVICE_SPEED_HIGH) { read_size = 64; } else { log_debug("Encountered unknown USB speed in %s\n", __FUNCTION__); return BLADERF_ERR_UNEXPECTED; } if (read_operation == BLADE_USB_CMD_FLASH_READ || read_operation == BLADE_USB_CMD_READ_OTP) { status = vendor_cmd_int_windex(dev, read_operation, page, &op_status); if (status != 0) { return status; } else if (op_status != 0) { log_error("Firmware page read (op=%d) failed at page %u: %d\n", read_operation, page, op_status); return BLADERF_ERR_UNEXPECTED; } /* Both of these operations require a read from the FW's page buffer */ request = BLADE_USB_CMD_READ_PAGE_BUFFER; } else if (read_operation == BLADE_USB_CMD_READ_CAL_CACHE) { request = read_operation; } else { assert(!"Bug - invalid read_operation value"); return BLADERF_ERR_UNEXPECTED; } /* Retrieve data from the firmware page buffer */ for (offset = 0; offset < dev->flash_arch->psize_bytes; offset += read_size) { status = usb->fn->control_transfer(usb->driver, USB_TARGET_DEVICE, USB_REQUEST_VENDOR, USB_DIR_DEVICE_TO_HOST, request, 0, offset, /* in bytes */ buf + offset, read_size, CTRL_TIMEOUT_MS); if(status < 0) { log_debug("Failed to read page buffer at offset 0x%02x: %s\n", offset, bladerf_strerror(status)); return status; } } return 0; } static int usb_read_flash_pages(struct bladerf *dev, uint8_t *buf, uint32_t page_u32, uint32_t count_u32) { int status; size_t n_read; uint16_t i; /* 16-bit control transfer fields are used for these. * The current bladeRF build only has a 4MiB flash, anyway. */ const uint16_t page = (uint16_t)page_u32; const uint16_t count = (uint16_t)count_u32; assert(page == page_u32); assert(count == count_u32); status = change_setting(dev, USB_IF_SPI_FLASH); if (status != 0) { return status; } log_info("Reading %u page%s starting at page %u\n", count, 1 == count ? "" : "s", page); for (n_read = i = 0; i < count; i++) { log_info("Reading page %u (%u%%)...%c", page + i, (i + 1) == count ? 100 : 100 * i / count, (i + 1) == count ? '\n' : '\r'); status = read_page(dev, BLADE_USB_CMD_FLASH_READ, page + i, buf + n_read); if (status != 0) { goto error; } n_read += dev->flash_arch->psize_bytes; } log_info("Done reading %u page%s\n", count, 1 == count ? "" : "s"); error: status = restore_post_flash_setting(dev); return status; } static int write_page(struct bladerf *dev, uint8_t write_operation, uint16_t page, const uint8_t *buf) { int status; int32_t commit_status; uint16_t offset; uint16_t write_size; struct bladerf_usb *usb = dev->backend_data; bladerf_dev_speed usb_speed; if (usb->fn->get_speed(usb->driver, &usb_speed) != 0) { log_debug("Error getting USB speed in %s\n", __FUNCTION__); return BLADERF_ERR_UNEXPECTED; } if (usb_speed == BLADERF_DEVICE_SPEED_SUPER) { write_size = dev->flash_arch->psize_bytes; } else if (usb_speed == BLADERF_DEVICE_SPEED_HIGH) { write_size = 64; } else { assert(!"BUG - unexpected device speed"); return BLADERF_ERR_UNEXPECTED; } /* Write the data to the firmware's page buffer. * Casting away the buffer's const-ness here is gross, but this buffer * will not be written to on an out transfer. */ for (offset = 0; offset < dev->flash_arch->psize_bytes; offset += write_size) { status = usb->fn->control_transfer(usb->driver, USB_TARGET_DEVICE, USB_REQUEST_VENDOR, USB_DIR_HOST_TO_DEVICE, BLADE_USB_CMD_WRITE_PAGE_BUFFER, 0, offset, (uint8_t*)&buf[offset], write_size, CTRL_TIMEOUT_MS); if(status < 0) { log_error("Failed to write page buffer at offset 0x%02x " "for page %u: %s\n", offset, page, bladerf_strerror(status)); return status; } } /* Commit the page to flash */ status = vendor_cmd_int_windex(dev, write_operation, page, &commit_status); if (status != 0) { log_error("Failed to commit page %u: %s\n", page, bladerf_strerror(status)); return status; } else if (commit_status != 0) { log_error("Failed to commit page %u, FW returned %d\n", page, commit_status); return BLADERF_ERR_UNEXPECTED; } return 0; } static int usb_write_flash_pages(struct bladerf *dev, const uint8_t *buf, uint32_t page_u32, uint32_t count_u32) { int status, restore_status; uint16_t i; size_t n_written; /* 16-bit control transfer fields are used for these. * The current bladeRF build only has a 4MiB flash, anyway. */ const uint16_t page = (uint16_t)page_u32; const uint16_t count = (uint16_t)count_u32; assert(page == page_u32); assert(count == count_u32); status = change_setting(dev, USB_IF_SPI_FLASH); if (status != 0) { return status; } log_info("Writing %u page%s starting at page %u\n", count, 1 == count ? "" : "s", page); n_written = 0; for (i = 0; i < count; i++) { log_info("Writing page %u (%u%%)...%c", page + i, (i + 1) == count ? 100 : 100 * i / count, (i + 1) == count ? '\n' : '\r'); status = write_page(dev, BLADE_USB_CMD_FLASH_WRITE, page + i, buf + n_written); if (status) { goto error; } n_written += dev->flash_arch->psize_bytes; } log_info("Done writing %u page%s\n", count, 1 == count ? "" : "s"); error: restore_status = restore_post_flash_setting(dev); if (status != 0) { return status; } else if (restore_status != 0) { return restore_status; } else { return 0; } } static int usb_device_reset(struct bladerf *dev) { struct bladerf_usb *usb = dev->backend_data; return usb->fn->control_transfer(usb->driver, USB_TARGET_DEVICE, USB_REQUEST_VENDOR, USB_DIR_HOST_TO_DEVICE, BLADE_USB_CMD_RESET, 0, 0, 0, 0, CTRL_TIMEOUT_MS); } static int usb_jump_to_bootloader(struct bladerf *dev) { struct bladerf_usb *usb = dev->backend_data; return usb->fn->control_transfer(usb->driver, USB_TARGET_DEVICE, USB_REQUEST_VENDOR, USB_DIR_HOST_TO_DEVICE, BLADE_USB_CMD_JUMP_TO_BOOTLOADER, 0, 0, 0, 0, CTRL_TIMEOUT_MS); } static int usb_get_cal(struct bladerf *dev, char *cal) { const uint16_t dummy_page = 0; int status, restore_status; assert(CAL_BUFFER_SIZE == dev->flash_arch->psize_bytes); status = change_setting(dev, USB_IF_SPI_FLASH); if (status) { return status; } status = read_page(dev, BLADE_USB_CMD_READ_CAL_CACHE, dummy_page, (uint8_t*)cal); restore_status = restore_post_flash_setting(dev); return status == 0 ? restore_status : status; } static int usb_get_otp(struct bladerf *dev, char *otp) { int status, restore_status; const uint16_t dummy_page = 0; status = change_setting(dev, USB_IF_SPI_FLASH); if (status) { return status; } status = read_page(dev, BLADE_USB_CMD_READ_OTP, dummy_page, (uint8_t*)otp); restore_status = restore_post_flash_setting(dev); return status == 0 ? restore_status : status; } static int usb_write_otp(struct bladerf *dev, char *otp) { int status, restore_status; const uint16_t dummy_page = 0; status = change_setting(dev, USB_IF_SPI_FLASH); if (status) { return status; } status = write_page(dev, BLADE_USB_CMD_WRITE_OTP, dummy_page, (uint8_t*)otp); restore_status = restore_post_flash_setting(dev); return status == 0 ? restore_status : status; } static int usb_lock_otp(struct bladerf *dev) { int status, restore_status, commit_status; status = change_setting(dev, USB_IF_SPI_FLASH); if (status) { return status; } status = vendor_cmd_int_windex(dev, BLADE_USB_CMD_LOCK_OTP, 0, &commit_status); if (commit_status != 0) { log_error("Failed to lock OTP, FW returned %d\n", commit_status); if (status == 0) status = commit_status; } restore_status = restore_post_flash_setting(dev); return status == 0 ? restore_status : status; } static int usb_get_device_speed(struct bladerf *dev, bladerf_dev_speed *speed) { struct bladerf_usb *usb = dev->backend_data; return usb->fn->get_speed(usb->driver, speed); } static int usb_set_firmware_loopback(struct bladerf *dev, bool enable) { int result; int status; status = vendor_cmd_int_wvalue(dev, BLADE_USB_CMD_SET_LOOPBACK, enable, &result); if (status != 0) { return status; } status = change_setting(dev, USB_IF_NULL); if (status == 0) { status = change_setting(dev, USB_IF_RF_LINK); } return status; } static int usb_get_firmware_loopback(struct bladerf *dev, bool *is_enabled) { int status, result; status = vendor_cmd_int(dev, BLADE_USB_CMD_GET_LOOPBACK, USB_DIR_DEVICE_TO_HOST, &result); if (status == 0) { *is_enabled = (result != 0); } return status; } static int usb_enable_module(struct bladerf *dev, bladerf_direction dir, bool enable) { int status; int32_t fx3_ret = -1; const uint16_t val = enable ? 1 : 0; const uint8_t cmd = (dir == BLADERF_RX) ? BLADE_USB_CMD_RF_RX : BLADE_USB_CMD_RF_TX; status = vendor_cmd_int_wvalue(dev, cmd, val, &fx3_ret); if (status != 0) { log_debug("Could not enable RF %s (%d): %s\n", (dir == BLADERF_RX) ? "RX" : "TX", status, bladerf_strerror(status)); } else if (fx3_ret != 0) { log_warning("FX3 reported error=0x%x when %s RF %s\n", fx3_ret, enable ? "enabling" : "disabling", (dir == BLADERF_RX) ? "RX" : "TX"); /* FIXME: Work around what seems to be a harmless failure. * It appears that in firmware or in the lib, we may be * attempting to disable an already disabled channel, or * enabling an already enabled channel. * * Further investigation required * * 0x44 corresponds to CY_U3P_ERROR_ALREADY_STARTED */ if (fx3_ret != 0x44) { status = BLADERF_ERR_UNEXPECTED; } } return status; } static int usb_init_stream(struct bladerf_stream *stream, size_t num_transfers) { struct bladerf_usb *usb = stream->dev->backend_data; return usb->fn->init_stream(usb->driver, stream, num_transfers); } static int usb_stream(struct bladerf_stream *stream, bladerf_channel_layout layout) { struct bladerf_usb *usb = stream->dev->backend_data; return usb->fn->stream(usb->driver, stream, layout); } int usb_submit_stream_buffer(struct bladerf_stream *stream, void *buffer, size_t *length, unsigned int timeout_ms, bool nonblock) { struct bladerf_usb *usb = stream->dev->backend_data; return usb->fn->submit_stream_buffer(usb->driver, stream, buffer, length, timeout_ms, nonblock); } static void usb_deinit_stream(struct bladerf_stream *stream) { struct bladerf_usb *usb = stream->dev->backend_data; usb->fn->deinit_stream(usb->driver, stream); } /* * Information about the boot image format and boot over USB can be found in * Cypress AN76405: EZ-USB (R) FX3 (TM) Boot Options: * http://www.cypress.com/?docID=49862 * * There's a request (bRequset = 0xc0) for the bootloader revision. * However, there doesn't appear to be any documented reason to check this and * behave differently depending upon the returned value. */ /* Command fields for FX3 firmware upload vendor requests */ #define FX3_BOOTLOADER_LOAD_BREQUEST 0xa0 #define FX3_BOOTLOADER_ADDR_WVALUE(addr) (HOST_TO_LE16(addr & 0xffff)) #define FX3_BOOTLOADER_ADDR_WINDEX(addr) (HOST_TO_LE16(((addr >> 16) & 0xffff))) #define FX3_BOOTLOADER_MAX_LOAD_LEN 4096 static int write_and_verify_fw_chunk(struct bladerf_usb *usb, uint32_t addr, uint8_t *data, uint32_t len, uint8_t *readback_buf) { int status; log_verbose("Writing %u bytes to bootloader @ 0x%08x\n", len, addr); status = usb->fn->control_transfer(usb->driver, USB_TARGET_DEVICE, USB_REQUEST_VENDOR, USB_DIR_HOST_TO_DEVICE, FX3_BOOTLOADER_LOAD_BREQUEST, FX3_BOOTLOADER_ADDR_WVALUE(addr), FX3_BOOTLOADER_ADDR_WINDEX(addr), data, len, CTRL_TIMEOUT_MS); if (status != 0) { log_debug("Failed to write FW chunk (%d)\n", status); return status; } log_verbose("Reading back %u bytes from bootloader @ 0x%08x\n", len, addr); status = usb->fn->control_transfer(usb->driver, USB_TARGET_DEVICE, USB_REQUEST_VENDOR, USB_DIR_DEVICE_TO_HOST, FX3_BOOTLOADER_LOAD_BREQUEST, FX3_BOOTLOADER_ADDR_WVALUE(addr), FX3_BOOTLOADER_ADDR_WINDEX(addr), readback_buf, len, CTRL_TIMEOUT_MS); if (status != 0) { log_debug("Failed to read back FW chunk (%d)\n", status); return status; } if (memcmp(data, readback_buf, len) != 0) { log_debug("Readback did match written data.\n"); status = BLADERF_ERR_UNEXPECTED; } return status; } static int execute_fw_from_bootloader(struct bladerf_usb *usb, uint32_t addr) { int status; status = usb->fn->control_transfer(usb->driver, USB_TARGET_DEVICE, USB_REQUEST_VENDOR, USB_DIR_HOST_TO_DEVICE, FX3_BOOTLOADER_LOAD_BREQUEST, FX3_BOOTLOADER_ADDR_WVALUE(addr), FX3_BOOTLOADER_ADDR_WINDEX(addr), NULL, 0, CTRL_TIMEOUT_MS); if (status != 0 && status != BLADERF_ERR_IO) { log_debug("Failed to exec firmware: %s\n:", bladerf_strerror(status)); } else if (status == BLADERF_ERR_IO) { /* The device might drop out from underneath us as it starts executing * the new firmware */ log_verbose("Device returned IO error due to FW boot.\n"); status = 0; } else { log_verbose("Booting new FW.\n"); } return status; } static int write_fw_to_bootloader(void *driver, struct fx3_firmware *fw) { int status = 0; uint32_t to_write; uint32_t data_len; uint32_t addr; uint8_t *data; bool got_section; uint8_t *readback = malloc(FX3_BOOTLOADER_MAX_LOAD_LEN); if (readback == NULL) { return BLADERF_ERR_MEM; } do { got_section = fx3_fw_next_section(fw, &addr, &data, &data_len); if (got_section) { /* data_len should never be zero, as fw->num_sections should NOT * include the terminating section in its count */ assert(data_len != 0); do { to_write = u32_min(data_len, FX3_BOOTLOADER_MAX_LOAD_LEN); status = write_and_verify_fw_chunk(driver, addr, data, to_write, readback); data_len -= to_write; addr += to_write; data += to_write; } while (data_len != 0 && status == 0); } } while (got_section && status == 0); if (status == 0) { status = execute_fw_from_bootloader(driver, fx3_fw_entry_point(fw)); } free(readback); return status; } static int usb_load_fw_from_bootloader(bladerf_backend backend, uint8_t bus, uint8_t addr, struct fx3_firmware *fw) { int status = 0; size_t i; struct bladerf_usb usb; for (i = 0; i < ARRAY_SIZE(usb_driver_list); i++) { if ((backend == BLADERF_BACKEND_ANY) || (usb_driver_list[i]->id == backend)) { usb.fn = usb_driver_list[i]->fn; status = usb.fn->open_bootloader(&usb.driver, bus, addr); if (status == 0) { status = write_fw_to_bootloader(&usb, fw); usb.fn->close_bootloader(usb.driver); break; } } } return status; } /* Default handlers for operations unsupported by the NIOS II legacy packet * format */ static int set_vctcxo_tamer_mode_unsupported(struct bladerf *dev, bladerf_vctcxo_tamer_mode mode) { log_debug("Operation not supported with legacy NIOS packet format.\n"); return BLADERF_ERR_UNSUPPORTED; } static int get_vctcxo_tamer_mode_unsupported(struct bladerf *dev, bladerf_vctcxo_tamer_mode *mode) { *mode = BLADERF_VCTCXO_TAMER_INVALID; log_debug("Operation not supported with legacy NIOS packet format.\n"); return BLADERF_ERR_UNSUPPORTED; } static int usb_read_fw_log(struct bladerf *dev, logger_entry *e) { int status; *e = LOG_EOF; status = vendor_cmd_int(dev, BLADE_USB_CMD_READ_LOG_ENTRY, USB_DIR_DEVICE_TO_HOST, (int32_t*) e); return status; } static int config_gpio_write(struct bladerf *dev, uint32_t val) { struct bladerf_usb *usb = dev->backend_data; bladerf_dev_speed usb_speed; if (usb->fn->get_speed(usb->driver, &usb_speed) != 0) { log_debug("Error getting USB speed in %s\n", __FUNCTION__); return BLADERF_ERR_UNEXPECTED; } /* If we're connected at HS, we need to use smaller DMA transfers */ if (usb_speed == BLADERF_DEVICE_SPEED_HIGH) { val |= BLADERF_GPIO_FEATURE_SMALL_DMA_XFER; } else if (usb_speed == BLADERF_DEVICE_SPEED_SUPER) { val &= ~BLADERF_GPIO_FEATURE_SMALL_DMA_XFER; } else { assert(!"Encountered unknown USB speed"); return BLADERF_ERR_UNEXPECTED; } return nios_config_write(dev, val); } static int set_agc_dc_correction_unsupported(struct bladerf *dev, int16_t q_max, int16_t i_max, int16_t q_mid, int16_t i_mid, int16_t q_low, int16_t i_low) { log_debug("Operation not supported with legacy NIOS packet format.\n"); return BLADERF_ERR_UNSUPPORTED; } static int legacy_config_gpio_write(struct bladerf *dev, uint32_t val) { struct bladerf_usb *usb = dev->backend_data; bladerf_dev_speed usb_speed; if (usb->fn->get_speed(usb->driver, &usb_speed) != 0) { log_debug("Error getting USB speed in %s\n", __FUNCTION__); return BLADERF_ERR_UNEXPECTED; } /* If we're connected at HS, we need to use smaller DMA transfers */ if (usb_speed == BLADERF_DEVICE_SPEED_HIGH) { val |= BLADERF_GPIO_FEATURE_SMALL_DMA_XFER; } else if (usb_speed == BLADERF_DEVICE_SPEED_SUPER) { val &= ~BLADERF_GPIO_FEATURE_SMALL_DMA_XFER; } else { assert(!"Encountered unknown USB speed"); return BLADERF_ERR_UNEXPECTED; } return nios_legacy_config_write(dev, val); } /* USB backend that used legacy format for communicating with NIOS II */ const struct backend_fns backend_fns_usb_legacy = { FIELD_INIT(.matches, usb_matches), FIELD_INIT(.probe, usb_probe), FIELD_INIT(.get_vid_pid, usb_get_vid_pid), FIELD_INIT(.get_flash_id, usb_get_flash_id), FIELD_INIT(.open, usb_open), FIELD_INIT(.set_fpga_protocol, usb_set_fpga_protocol), FIELD_INIT(.close, usb_close), FIELD_INIT(.is_fw_ready, usb_is_fw_ready), FIELD_INIT(.get_handle, usb_get_handle), FIELD_INIT(.load_fpga, usb_load_fpga), FIELD_INIT(.is_fpga_configured, usb_is_fpga_configured), FIELD_INIT(.get_fpga_source, usb_get_fpga_source), FIELD_INIT(.get_fw_version, usb_get_fw_version), FIELD_INIT(.get_fpga_version, usb_get_fpga_version), FIELD_INIT(.erase_flash_blocks, usb_erase_flash_blocks), FIELD_INIT(.read_flash_pages, usb_read_flash_pages), FIELD_INIT(.write_flash_pages, usb_write_flash_pages), FIELD_INIT(.device_reset, usb_device_reset), FIELD_INIT(.jump_to_bootloader, usb_jump_to_bootloader), FIELD_INIT(.get_cal, usb_get_cal), FIELD_INIT(.get_otp, usb_get_otp), FIELD_INIT(.write_otp, usb_write_otp), FIELD_INIT(.lock_otp, usb_lock_otp), FIELD_INIT(.get_device_speed, usb_get_device_speed), FIELD_INIT(.config_gpio_write, legacy_config_gpio_write), FIELD_INIT(.config_gpio_read, nios_legacy_config_read), FIELD_INIT(.expansion_gpio_write, nios_legacy_expansion_gpio_write), FIELD_INIT(.expansion_gpio_read, nios_legacy_expansion_gpio_read), FIELD_INIT(.expansion_gpio_dir_write, nios_legacy_expansion_gpio_dir_write), FIELD_INIT(.expansion_gpio_dir_read, nios_legacy_expansion_gpio_dir_read), FIELD_INIT(.set_iq_gain_correction, nios_legacy_set_iq_gain_correction), FIELD_INIT(.set_iq_phase_correction, nios_legacy_set_iq_phase_correction), FIELD_INIT(.get_iq_gain_correction, nios_legacy_get_iq_gain_correction), FIELD_INIT(.get_iq_phase_correction, nios_legacy_get_iq_phase_correction), FIELD_INIT(.set_agc_dc_correction, set_agc_dc_correction_unsupported), FIELD_INIT(.get_timestamp, nios_legacy_get_timestamp), FIELD_INIT(.si5338_write, nios_legacy_si5338_write), FIELD_INIT(.si5338_read, nios_legacy_si5338_read), FIELD_INIT(.lms_write, nios_legacy_lms6_write), FIELD_INIT(.lms_read, nios_legacy_lms6_read), FIELD_INIT(.ina219_write, nios_legacy_ina219_write), FIELD_INIT(.ina219_read, nios_legacy_ina219_read), FIELD_INIT(.ad9361_spi_write, nios_legacy_ad9361_spi_write), FIELD_INIT(.ad9361_spi_read, nios_legacy_ad9361_spi_read), FIELD_INIT(.adi_axi_write, nios_legacy_adi_axi_write), FIELD_INIT(.adi_axi_read, nios_legacy_adi_axi_read), FIELD_INIT(.rfic_command_write, nios_legacy_rfic_command_write), FIELD_INIT(.rfic_command_read, nios_legacy_rfic_command_read), FIELD_INIT(.rffe_control_write, nios_legacy_rffe_control_write), FIELD_INIT(.rffe_control_read, nios_legacy_rffe_control_read), FIELD_INIT(.rffe_fastlock_save, nios_legacy_rffe_fastlock_save), FIELD_INIT(.ad56x1_vctcxo_trim_dac_write, nios_legacy_ad56x1_vctcxo_trim_dac_write), FIELD_INIT(.ad56x1_vctcxo_trim_dac_read, nios_legacy_ad56x1_vctcxo_trim_dac_read), FIELD_INIT(.adf400x_write, nios_legacy_adf400x_write), FIELD_INIT(.adf400x_read, nios_legacy_adf400x_read), FIELD_INIT(.vctcxo_dac_write, nios_legacy_vctcxo_trim_dac_write), FIELD_INIT(.vctcxo_dac_read, nios_vctcxo_trim_dac_read), FIELD_INIT(.set_vctcxo_tamer_mode, set_vctcxo_tamer_mode_unsupported), FIELD_INIT(.get_vctcxo_tamer_mode, get_vctcxo_tamer_mode_unsupported), FIELD_INIT(.xb_spi, nios_legacy_xb200_synth_write), FIELD_INIT(.set_firmware_loopback, usb_set_firmware_loopback), FIELD_INIT(.get_firmware_loopback, usb_get_firmware_loopback), FIELD_INIT(.enable_module, usb_enable_module), FIELD_INIT(.init_stream, usb_init_stream), FIELD_INIT(.stream, usb_stream), FIELD_INIT(.submit_stream_buffer, usb_submit_stream_buffer), FIELD_INIT(.deinit_stream, usb_deinit_stream), FIELD_INIT(.retune, nios_retune), FIELD_INIT(.retune2, nios_retune2), FIELD_INIT(.load_fw_from_bootloader, usb_load_fw_from_bootloader), FIELD_INIT(.read_fw_log, usb_read_fw_log), FIELD_INIT(.read_trigger, nios_legacy_read_trigger), FIELD_INIT(.write_trigger, nios_legacy_write_trigger), FIELD_INIT(.name, "usb"), }; /* USB backend for use with FPGA supporting update NIOS II packet formats */ const struct backend_fns backend_fns_usb = { FIELD_INIT(.matches, usb_matches), FIELD_INIT(.probe, usb_probe), FIELD_INIT(.get_vid_pid, usb_get_vid_pid), FIELD_INIT(.get_flash_id, usb_get_flash_id), FIELD_INIT(.open, usb_open), FIELD_INIT(.set_fpga_protocol, usb_set_fpga_protocol), FIELD_INIT(.close, usb_close), FIELD_INIT(.is_fw_ready, usb_is_fw_ready), FIELD_INIT(.get_handle, usb_get_handle), FIELD_INIT(.load_fpga, usb_load_fpga), FIELD_INIT(.is_fpga_configured, usb_is_fpga_configured), FIELD_INIT(.get_fpga_source, usb_get_fpga_source), FIELD_INIT(.get_fw_version, usb_get_fw_version), FIELD_INIT(.get_fpga_version, usb_get_fpga_version), FIELD_INIT(.erase_flash_blocks, usb_erase_flash_blocks), FIELD_INIT(.read_flash_pages, usb_read_flash_pages), FIELD_INIT(.write_flash_pages, usb_write_flash_pages), FIELD_INIT(.device_reset, usb_device_reset), FIELD_INIT(.jump_to_bootloader, usb_jump_to_bootloader), FIELD_INIT(.get_cal, usb_get_cal), FIELD_INIT(.get_otp, usb_get_otp), FIELD_INIT(.write_otp, usb_write_otp), FIELD_INIT(.lock_otp, usb_lock_otp), FIELD_INIT(.get_device_speed, usb_get_device_speed), FIELD_INIT(.config_gpio_write, config_gpio_write), FIELD_INIT(.config_gpio_read, nios_config_read), FIELD_INIT(.expansion_gpio_write, nios_expansion_gpio_write), FIELD_INIT(.expansion_gpio_read, nios_expansion_gpio_read), FIELD_INIT(.expansion_gpio_dir_write, nios_expansion_gpio_dir_write), FIELD_INIT(.expansion_gpio_dir_read, nios_expansion_gpio_dir_read), FIELD_INIT(.set_iq_gain_correction, nios_set_iq_gain_correction), FIELD_INIT(.set_iq_phase_correction, nios_set_iq_phase_correction), FIELD_INIT(.get_iq_gain_correction, nios_get_iq_gain_correction), FIELD_INIT(.get_iq_phase_correction, nios_get_iq_phase_correction), FIELD_INIT(.set_agc_dc_correction, nios_set_agc_dc_correction), FIELD_INIT(.get_timestamp, nios_get_timestamp), FIELD_INIT(.si5338_write, nios_si5338_write), FIELD_INIT(.si5338_read, nios_si5338_read), FIELD_INIT(.lms_write, nios_lms6_write), FIELD_INIT(.lms_read, nios_lms6_read), FIELD_INIT(.ina219_write, nios_ina219_write), FIELD_INIT(.ina219_read, nios_ina219_read), FIELD_INIT(.ad9361_spi_write, nios_ad9361_spi_write), FIELD_INIT(.ad9361_spi_read, nios_ad9361_spi_read), FIELD_INIT(.adi_axi_write, nios_adi_axi_write), FIELD_INIT(.adi_axi_read, nios_adi_axi_read), FIELD_INIT(.wishbone_master_write, nios_wishbone_master_write), FIELD_INIT(.wishbone_master_read, nios_wishbone_master_read), FIELD_INIT(.rfic_command_write, nios_rfic_command_write), FIELD_INIT(.rfic_command_read, nios_rfic_command_read), FIELD_INIT(.rffe_control_write, nios_rffe_control_write), FIELD_INIT(.rffe_control_read, nios_rffe_control_read), FIELD_INIT(.rffe_fastlock_save, nios_rffe_fastlock_save), FIELD_INIT(.ad56x1_vctcxo_trim_dac_write, nios_ad56x1_vctcxo_trim_dac_write), FIELD_INIT(.ad56x1_vctcxo_trim_dac_read, nios_ad56x1_vctcxo_trim_dac_read), FIELD_INIT(.adf400x_write, nios_adf400x_write), FIELD_INIT(.adf400x_read, nios_adf400x_read), FIELD_INIT(.vctcxo_dac_write, nios_vctcxo_trim_dac_write), FIELD_INIT(.vctcxo_dac_read, nios_vctcxo_trim_dac_read), FIELD_INIT(.set_vctcxo_tamer_mode, nios_set_vctcxo_tamer_mode), FIELD_INIT(.get_vctcxo_tamer_mode, nios_get_vctcxo_tamer_mode), FIELD_INIT(.xb_spi, nios_xb200_synth_write), FIELD_INIT(.set_firmware_loopback, usb_set_firmware_loopback), FIELD_INIT(.get_firmware_loopback, usb_get_firmware_loopback), FIELD_INIT(.enable_module, usb_enable_module), FIELD_INIT(.init_stream, usb_init_stream), FIELD_INIT(.stream, usb_stream), FIELD_INIT(.submit_stream_buffer, usb_submit_stream_buffer), FIELD_INIT(.deinit_stream, usb_deinit_stream), FIELD_INIT(.retune, nios_retune), FIELD_INIT(.retune2, nios_retune2), FIELD_INIT(.load_fw_from_bootloader, usb_load_fw_from_bootloader), FIELD_INIT(.read_fw_log, usb_read_fw_log), FIELD_INIT(.read_trigger, nios_read_trigger), FIELD_INIT(.write_trigger, nios_write_trigger), FIELD_INIT(.name, "usb"), };