diff --git a/host/libhackrf/CMakeLists.txt b/host/libhackrf/CMakeLists.txt new file mode 100644 index 00000000..370a1c2d --- /dev/null +++ b/host/libhackrf/CMakeLists.txt @@ -0,0 +1,41 @@ +# Copyright 2012 Jared Boone +# +# This file is part of HackRF. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# This program 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +# Based heavily upon the libftdi cmake setup. + +project(libhackrf) +set(MAJOR_VERSION 0) +set(MINOR_VERSION 1) +set(PACKAGE libhackrf) +set(VERSION_STRING ${MAJOR_VERSION}.${MINOR_VERSION}) +set(VERSION ${VERSION_STRING}) +set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}") + +cmake_minimum_required(VERSION 2.8) + +add_definitions(-Wall) +add_definitions(-std=c99) + +find_package(USB1 REQUIRED) +include_directories(${LIBUSB_INCLUDE_DIR}) + +add_subdirectory(src) +add_subdirectory(examples) + diff --git a/host/libhackrf/FindUSB1.cmake b/host/libhackrf/FindUSB1.cmake new file mode 100644 index 00000000..0cbf8022 --- /dev/null +++ b/host/libhackrf/FindUSB1.cmake @@ -0,0 +1,38 @@ +# - Try to find the freetype library +# Once done this defines +# +# LIBUSB_FOUND - system has libusb +# LIBUSB_INCLUDE_DIR - the libusb include directory +# LIBUSB_LIBRARIES - Link these to use libusb + +# Copyright (c) 2006, 2008 Laurent Montel, +# +# Redistribution and use is allowed according to the terms of the BSD license. +# For details see the accompanying COPYING-CMAKE-SCRIPTS file. + + +if (LIBUSB_INCLUDE_DIR AND LIBUSB_LIBRARIES) + + # in cache already + set(LIBUSB_FOUND TRUE) + +else (LIBUSB_INCLUDE_DIR AND LIBUSB_LIBRARIES) + IF (NOT WIN32) + # use pkg-config to get the directories and then use these values + # in the FIND_PATH() and FIND_LIBRARY() calls + find_package(PkgConfig) + pkg_check_modules(PC_LIBUSB libusb-1.0) + ENDIF(NOT WIN32) + + FIND_PATH(LIBUSB_INCLUDE_DIR libusb.h + PATHS ${PC_LIBUSB_INCLUDEDIR} ${PC_LIBUSB_INCLUDE_DIRS}) + + FIND_LIBRARY(LIBUSB_LIBRARIES NAMES usb-1.0 + PATHS ${PC_LIBUSB_LIBDIR} ${PC_LIBUSB_LIBRARY_DIRS}) + + include(FindPackageHandleStandardArgs) + FIND_PACKAGE_HANDLE_STANDARD_ARGS(LIBUSB DEFAULT_MSG LIBUSB_LIBRARIES LIBUSB_INCLUDE_DIR) + + MARK_AS_ADVANCED(LIBUSB_INCLUDE_DIR LIBUSB_LIBRARIES) + +endif (LIBUSB_INCLUDE_DIR AND LIBUSB_LIBRARIES) \ No newline at end of file diff --git a/host/libhackrf/examples/CMakeLists.txt b/host/libhackrf/examples/CMakeLists.txt new file mode 100644 index 00000000..fc3a198d --- /dev/null +++ b/host/libhackrf/examples/CMakeLists.txt @@ -0,0 +1,33 @@ +# Copyright 2012 Jared Boone +# +# This file is part of HackRF. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# This program 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +# Based heavily upon the libftdi cmake setup. + +option(EXAMPLES "Build example programs" ON) + +IF( EXAMPLES ) + add_executable(hackrf_max2837 hackrf_max2837.c) + add_executable(hackrf_transfer hackrf_transfer.c) + + target_link_libraries(hackrf_max2837 hackrf) + target_link_libraries(hackrf_transfer hackrf) + + include_directories(BEFORE ${CMAKE_SOURCE_DIR}/src) +endif(EXAMPLES) diff --git a/host/libhackrf/examples/hackrf_max2837.c b/host/libhackrf/examples/hackrf_max2837.c new file mode 100644 index 00000000..f4fbdf2e --- /dev/null +++ b/host/libhackrf/examples/hackrf_max2837.c @@ -0,0 +1,161 @@ +/* + * Copyright 2012 Jared Boone + * + * This file is part of HackRF. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#include + +#include +#include +#include + +static void usage() { + printf("\nUsage:\n"); + printf("\t-n, --register : set register number for subsequent read/write operations\n"); + printf("\t-r, --read: read register specified by last -n argument, or all registers\n"); + printf("\t-w, --write : write register specified by last -n argument with value \n"); + printf("\nExamples:\n"); + printf("\t -n 12 -r # reads from register 12\n"); + printf("\t -r # reads all registers\n"); + printf("\t -n 10 -w 22 # writes register 10 with 22 decimal\n"); +} + +static struct option long_options[] = { + { "register", required_argument, 0, 'n' }, + { "write", required_argument, 0, 'w' }, + { "read", no_argument, 0, 'r' }, + { 0, 0, 0, 0 }, +}; + +int parse_int(char* const s, uint16_t* const value) { + char* s_end = s; + const long long_value = strtol(s, &s_end, 10); + if( (s != s_end) && (*s_end == 0) ) { + *value = long_value; + return HACKRF_SUCCESS; + } else { + return HACKRF_ERROR_INVALID_PARAM; + } +} + +int dump_register(hackrf_device* device, const uint16_t register_number) { + uint16_t register_value; + int result = hackrf_max2837_read(device, register_number, ®ister_value); + + if( result == HACKRF_SUCCESS ) { + printf("[%2d] -> 0x%03x\n", register_number, register_value); + } else { + printf("hackrf_max2837_read() failed: %s (%d)\n", hackrf_error_name(result), result); + } + + return result; +} + +int dump_registers(hackrf_device* device) { + int result = HACKRF_SUCCESS; + + for(uint16_t register_number=0; register_number<32; register_number++) { + result = dump_register(device, register_number); + if( result != HACKRF_SUCCESS ) { + break; + } + } + + return result; +} + +int write_register( + hackrf_device* device, + const uint16_t register_number, + const uint16_t register_value +) { + int result = HACKRF_SUCCESS; + result = hackrf_max2837_write(device, register_number, register_value); + + if( result == HACKRF_SUCCESS ) { + printf("0x%03x -> [%2d]\n", register_value, register_number); + } else { + printf("hackrf_max2837_write() failed: %s (%d)\n", hackrf_error_name(result), result); + } + + return result; +} + +#define REGISTER_INVALID 32767 + +int main(int argc, char** argv) { + int opt; + uint16_t register_number = REGISTER_INVALID; + uint16_t register_value; + + int result = hackrf_init(); + if( result ) { + printf("hackrf_init() failed: %s (%d)\n", hackrf_error_name(result), result); + return -1; + } + + hackrf_device* device = NULL; + result = hackrf_open(&device); + if( result ) { + printf("hackrf_open() failed: %s (%d)\n", hackrf_error_name(result), result); + return -1; + } + + int option_index = 0; + while( (opt = getopt_long(argc, argv, "n:rw:", long_options, &option_index)) != EOF ) { + switch( opt ) { + case 'n': + result = parse_int(optarg, ®ister_number); + break; + + case 'w': + result = parse_int(optarg, ®ister_value); + if( result == HACKRF_SUCCESS ) { + result = write_register(device, register_number, register_value); + } + break; + + case 'r': + if( register_number == REGISTER_INVALID ) { + result = dump_registers(device); + } else { + result = dump_register(device, register_number); + } + break; + + default: + usage(); + } + + if( result != HACKRF_SUCCESS ) { + printf("argument error: %s (%d)\n", hackrf_error_name(result), result); + break; + } + } + + result = hackrf_close(device); + if( result ) { + printf("hackrf_close() failed: %s (%d)\n", hackrf_error_name(result), result); + return -1; + } + + hackrf_exit(); + + return 0; +} diff --git a/host/libhackrf/examples/hackrf_transfer.c b/host/libhackrf/examples/hackrf_transfer.c new file mode 100644 index 00000000..7bee7b51 --- /dev/null +++ b/host/libhackrf/examples/hackrf_transfer.c @@ -0,0 +1,210 @@ +/* + * Copyright 2012 Jared Boone + * + * This file is part of HackRF. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +typedef enum { + TRANSCEIVER_MODE_RX, + TRANSCEIVER_MODE_TX, +} transceiver_mode_t; +static transceiver_mode_t transceiver_mode = TRANSCEIVER_MODE_RX; + +static float +TimevalDiff(const struct timeval *a, const struct timeval *b) +{ + return (a->tv_sec - b->tv_sec) + 1e-6f * (a->tv_usec - b->tv_usec); +} + +int fd = -1; +volatile uint32_t byte_count = 0; + +int rx_callback(hackrf_transfer* transfer) { + if( fd != -1 ) { + byte_count += transfer->valid_length; + const ssize_t bytes_written = write(fd, transfer->buffer, transfer->valid_length); + if( bytes_written == transfer->valid_length ) { + return 0; + } else { + close(fd); + fd = -1; + return -1; + } + } else { + return -1; + } +} + +int tx_callback(hackrf_transfer* transfer) { + if( fd != -1 ) { + byte_count += transfer->valid_length; + const ssize_t bytes_read = read(fd, transfer->buffer, transfer->valid_length); + if( bytes_read == transfer->valid_length ) { + return 0; + } else { + close(fd); + fd = -1; + return -1; + } + } else { + return -1; + } +} + +static void usage() { + printf("Usage:\n"); + printf("\t-r # Receive data into file.\n"); + printf("\t-t # Transmit data from file.\n"); +} + +static hackrf_device* device = NULL; + +void sigint_callback_handler(int signum) { + hackrf_stop_rx(device); + hackrf_stop_tx(device); +} + +int main(int argc, char** argv) { + int opt; + bool receive = false; + bool transmit = false; + const char* path = NULL; + + while( (opt = getopt(argc, argv, "r:t:")) != EOF ) { + switch( opt ) { + case 'r': + receive = true; + path = optarg; + break; + + case 't': + transmit = true; + path = optarg; + break; + + default: + usage(); + return 1; + } + } + + if( transmit == receive ) { + if( transmit == true ) { + fprintf(stderr, "receive and transmit options are mutually exclusive\n"); + } else { + fprintf(stderr, "specify either transmit or receive option\n"); + } + return 1; + } + + if( receive ) { + transceiver_mode = TRANSCEIVER_MODE_RX; + } + + if( transmit ) { + transceiver_mode = TRANSCEIVER_MODE_TX; + } + + if( path == NULL ) { + fprintf(stderr, "specify a path to a file to transmit/receive\n"); + return 1; + } + + int result = hackrf_init(); + if( result ) { + printf("hackrf_init() failed: %s (%d)\n", hackrf_error_name(result), result); + return -1; + } + + result = hackrf_open(&device); + if( result ) { + printf("hackrf_open() failed: %s (%d)\n", hackrf_error_name(result), result); + return -1; + } + + fd = -1; + if( transceiver_mode == TRANSCEIVER_MODE_RX ) { + fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, S_IRWXU | S_IRWXG | S_IRWXO); + } else { + fd = open(path, O_RDONLY, S_IRWXU | S_IRWXG | S_IRWXO); + } + + if( fd == -1 ) { + printf("Failed to open file: errno %d\n", errno); + return fd; + } + + signal(SIGINT, sigint_callback_handler); + + if( transceiver_mode == TRANSCEIVER_MODE_RX ) { + result = hackrf_start_rx(device, rx_callback); + } else { + result = hackrf_start_tx(device, tx_callback); + } + if( result ) { + printf("hackrf_start_?x() failed: %s (%d)\n", hackrf_error_name(result), result); + return -1; + } + + struct timeval time_start; + gettimeofday(&time_start, NULL); + + while( hackrf_is_streaming(device) ) { + sleep(1); + + struct timeval time_now; + gettimeofday(&time_now, NULL); + + uint32_t byte_count_now = byte_count; + byte_count = 0; + + const float time_difference = TimevalDiff(&time_now, &time_start); + const float rate = (float)byte_count_now / time_difference; + printf("%4.1f MiB / %5.3f sec = %4.1f MiB/second\n", + byte_count_now / 1e6f, + time_difference, + rate / 1e6f + ); + + time_start = time_now; + } + + result = hackrf_close(device); + if( result ) { + printf("hackrf_close() failed: %s (%d)\n", hackrf_error_name(result), result); + return -1; + } + + hackrf_exit(); + + return 0; +} diff --git a/host/libhackrf/src/CMakeLists.txt b/host/libhackrf/src/CMakeLists.txt new file mode 100644 index 00000000..223d034c --- /dev/null +++ b/host/libhackrf/src/CMakeLists.txt @@ -0,0 +1,69 @@ +# Copyright 2012 Jared Boone +# +# This file is part of HackRF. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# This program 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +# Based heavily upon the libftdi cmake setup. + +# Targets +set(c_sources ${CMAKE_CURRENT_SOURCE_DIR}/hackrf.c CACHE INTERNAL "List of C sources") +set(c_headers ${CMAKE_CURRENT_SOURCE_DIR}/hackrf.h CACHE INTERNAL "List of C headers") + +# Dynamic library +add_library(hackrf SHARED ${c_sources}) +set_target_properties(hackrf PROPERTIES VERSION ${MAJOR_VERSION}.${MINOR_VERSION}.0 SOVERSION 0) + +# Static library +add_library(hackrf-static STATIC ${c_sources}) +set_target_properties(hackrf-static PROPERTIES OUTPUT_NAME "hackrf") + +set_target_properties(hackrf PROPERTIES CLEAN_DIRECT_OUTPUT 1) +set_target_properties(hackrf-static PROPERTIES CLEAN_DIRECT_OUTPUT 1) + +# Dependencies +target_link_libraries(hackrf ${LIBUSB_LIBRARIES}) + +if( ${UNIX} ) + install(TARGETS hackrf + LIBRARY DESTINATION lib${LIB_SUFFIX} + COMPONENT sharedlibs + ) + install(TARGETS hackrf-static + ARCHIVE DESTINATION lib${LIB_SUFFIX} + COMPONENT staticlibs + ) + install(FILES ${c_headers} + DESTINATION include/${PROJECT_NAME} + COMPONENT headers + ) +endif( ${UNIX} ) + +if( ${WIN32} ) + install(TARGETS hackrf + DESTINATION bin + COMPONENT sharedlibs + ) + install(TARGETS hackrf-static + DESTINATION bin + COMPONENT staticlibs + ) + install(FILES ${c_headers} + DESTINATION include/${PROJECT_NAME} + COMPONENT headers + ) +endif( ${WIN32} ) diff --git a/host/libhackrf/src/hackrf.c b/host/libhackrf/src/hackrf.c new file mode 100644 index 00000000..692580df --- /dev/null +++ b/host/libhackrf/src/hackrf.c @@ -0,0 +1,451 @@ +/* + * Copyright 2012 Jared Boone + * + * This file is part of HackRF. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#include "hackrf.h" + +#include + +#include +#include + +// TODO: Factor this into a shared #include so that firmware can use +// the same values. +typedef enum { + HACKRF_VENDOR_REQUEST_SET_TRANSCEIVER_MODE = 1, + HACKRF_VENDOR_REQUEST_MAX2837_WRITE = 2, + HACKRF_VENDOR_REQUEST_MAX2837_READ = 3, +} hackrf_vendor_request; + +typedef enum { + HACKRF_TRANSCEIVER_MODE_RECEIVE = 1, + HACKRF_TRANSCEIVER_MODE_TRANSMIT = 2, +} hackrf_transceiver_mode; + +struct hackrf_device { + libusb_device_handle* usb_device; + struct libusb_transfer** transfers; + hackrf_sample_block_cb_fn callback; + pthread_t transfer_thread; + uint32_t transfer_count; + uint32_t buffer_size; + bool streaming; +}; + +static const uint16_t hackrf_usb_vid = 0x1d50; +static const uint16_t hackrf_usb_pid = 0x604b; + +static libusb_context* g_libusb_context = NULL; + +static int free_transfers(hackrf_device* device) { + if( device->transfers != NULL ) { + // libusb_close() should free all transfers referenced from this array. + for(uint32_t transfer_index=0; transfer_indextransfer_count; transfer_index++) { + if( device->transfers[transfer_index] != NULL ) { + libusb_free_transfer(device->transfers[transfer_index]); + device->transfers[transfer_index] = NULL; + } + } + free(device->transfers); + device->transfers = NULL; + } + return HACKRF_SUCCESS; +} + +static int allocate_transfers(hackrf_device* const device) { + if( device->transfers == NULL ) { + device->transfers = calloc(device->transfer_count, sizeof(struct libusb_transfer)); + if( device->transfers == NULL ) { + return HACKRF_ERROR_NO_MEM; + } + + for(uint32_t transfer_index=0; transfer_indextransfer_count; transfer_index++) { + device->transfers[transfer_index] = libusb_alloc_transfer(0); + if( device->transfers[transfer_index] == NULL ) { + return HACKRF_ERROR_LIBUSB; + } + + libusb_fill_bulk_transfer( + device->transfers[transfer_index], + device->usb_device, + 0, + (unsigned char*)malloc(device->buffer_size), + device->buffer_size, + NULL, + device, + 0 + ); + + if( device->transfers[transfer_index]->buffer == NULL ) { + return HACKRF_ERROR_NO_MEM; + } + } + + return HACKRF_SUCCESS; + } else { + return HACKRF_ERROR_BUSY; + } +} + +static int prepare_transfers( + hackrf_device* device, + const uint_fast8_t endpoint_address, + libusb_transfer_cb_fn callback +) { + if( device->transfers != NULL ) { + for(uint32_t transfer_index=0; transfer_indextransfer_count; transfer_index++) { + device->transfers[transfer_index]->endpoint = endpoint_address; + device->transfers[transfer_index]->callback = callback; + + int error = libusb_submit_transfer(device->transfers[transfer_index]); + if( error != 0 ) { + return HACKRF_ERROR_LIBUSB; + } + } + return HACKRF_SUCCESS; + } else { + // This shouldn't happen. + return HACKRF_ERROR_OTHER; + } +} +/* +static int cancel_transfers(hackrf_device* device) { + if( device->transfers != NULL ) { + for(uint32_t transfer_index=0; transfer_indextransfer_count; transfer_index++) { + libusb_cancel_transfer(device->transfers[transfer_index]); + } + return HACKRF_SUCCESS; + } else { + // This shouldn't happen. + return HACKRF_ERROR_OTHER; + } +} +*/ +int hackrf_init() { + const int libusb_error = libusb_init(&g_libusb_context); + if( libusb_error != 0 ) { + return HACKRF_ERROR_LIBUSB; + } else { + return HACKRF_SUCCESS; + } +} + +int hackrf_exit() { + if( g_libusb_context != NULL ) { + libusb_exit(g_libusb_context); + g_libusb_context = NULL; + } + + return HACKRF_SUCCESS; +} + +int hackrf_open(hackrf_device** device) { + if( device == NULL ) { + return HACKRF_ERROR_INVALID_PARAM; + } + + // TODO: Do proper scanning of available devices, searching for + // unit serial number (if specified?). + libusb_device_handle* usb_device = libusb_open_device_with_vid_pid(g_libusb_context, hackrf_usb_vid, hackrf_usb_pid); + if( usb_device == NULL ) { + return HACKRF_ERROR_NOT_FOUND; + } + + //int speed = libusb_get_device_speed(usb_device); + // TODO: Error or warning if not high speed USB? + + int result = libusb_set_configuration(usb_device, 1); + if( result != 0 ) { + libusb_close(usb_device); + return HACKRF_ERROR_LIBUSB; + } + + result = libusb_claim_interface(usb_device, 0); + if( result != 0 ) { + libusb_close(usb_device); + return HACKRF_ERROR_LIBUSB; + } + + hackrf_device* lib_device = NULL; + lib_device = malloc(sizeof(*lib_device)); + if( lib_device == NULL ) { + libusb_release_interface(usb_device, 0); + libusb_close(usb_device); + return HACKRF_ERROR_NO_MEM; + } + + lib_device->usb_device = usb_device; + lib_device->transfers = NULL; + lib_device->callback = NULL; + lib_device->transfer_thread = 0; + lib_device->transfer_count = 1024; + lib_device->buffer_size = 16384; + lib_device->streaming = false; + + result = allocate_transfers(lib_device); + if( result != 0 ) { + free(lib_device); + libusb_release_interface(usb_device, 0); + libusb_close(usb_device); + return HACKRF_ERROR_NO_MEM; + } + + *device = lib_device; + + return HACKRF_SUCCESS; +} + +static int hackrf_set_transceiver_mode(hackrf_device* device, hackrf_transceiver_mode value) { + int result = libusb_control_transfer( + device->usb_device, + LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE, + HACKRF_VENDOR_REQUEST_SET_TRANSCEIVER_MODE, + value, + 0, + NULL, + 0, + 0 + ); + + if( result != 0 ) { + return HACKRF_ERROR_LIBUSB; + } else { + return HACKRF_SUCCESS; + } +} + +int hackrf_max2837_read(hackrf_device* device, uint8_t register_number, uint16_t* value) { + if( register_number >= 32 ) { + return HACKRF_ERROR_INVALID_PARAM; + } + + int result = libusb_control_transfer( + device->usb_device, + LIBUSB_ENDPOINT_IN | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE, + HACKRF_VENDOR_REQUEST_MAX2837_READ, + 0, + register_number, + (unsigned char*)value, + 2, + 0 + ); + + if( result < 2 ) { + return HACKRF_ERROR_LIBUSB; + } else { + return HACKRF_SUCCESS; + } +} + +int hackrf_max2837_write(hackrf_device* device, uint8_t register_number, uint16_t value) { + if( register_number >= 32 ) { + return HACKRF_ERROR_INVALID_PARAM; + } + if( value >= 0x400 ) { + return HACKRF_ERROR_INVALID_PARAM; + } + + int result = libusb_control_transfer( + device->usb_device, + LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE, + HACKRF_VENDOR_REQUEST_MAX2837_WRITE, + value, + register_number, + NULL, + 0, + 0 + ); + + if( result != 0 ) { + return HACKRF_ERROR_LIBUSB; + } else { + return HACKRF_SUCCESS; + } +} + +static void* transfer_threadproc(void* arg) { + hackrf_device* device = (hackrf_device*)arg; + + struct timeval timeout = { 0, 500000 }; + + while( device->streaming ) { + int error = libusb_handle_events_timeout(g_libusb_context, &timeout); + if( error != 0 ) { + device->streaming = false; + } + } + + return NULL; +} + +static void hackrf_libusb_transfer_callback(struct libusb_transfer* usb_transfer) { + hackrf_device* device = (hackrf_device*)usb_transfer->user_data; + + if( usb_transfer->status == LIBUSB_TRANSFER_COMPLETED ) { + hackrf_transfer transfer = { + .device = device, + .buffer = usb_transfer->buffer, + .buffer_length = usb_transfer->length, + .valid_length = usb_transfer->actual_length, + }; + + if( device->callback(&transfer) == 0 ) { + libusb_submit_transfer(usb_transfer); + return; + } + } + + device->streaming = false; +} + +static int kill_transfer_thread(hackrf_device* device) { + device->streaming = false; + + if( device->transfer_thread != 0 ) { + void* value = NULL; + int result = pthread_join(device->transfer_thread, &value); + if( result != 0 ) { + return HACKRF_ERROR_THREAD; + } + device->transfer_thread = 0; + } + + return HACKRF_SUCCESS; +} + +static int create_transfer_thread( + hackrf_device* device, + const uint8_t endpoint_address, + hackrf_sample_block_cb_fn callback +) { + if( device->transfer_thread == 0 ) { + int result = prepare_transfers( + device, endpoint_address, + hackrf_libusb_transfer_callback + ); + if( result != HACKRF_SUCCESS ) { + return result; + } + + device->callback = callback; + device->streaming = true; + + result = pthread_create(&device->transfer_thread, 0, transfer_threadproc, device); + if( result != 0 ) { + return HACKRF_ERROR_THREAD; + } + } else { + return HACKRF_ERROR_BUSY; + } + + return HACKRF_SUCCESS; +} + +bool hackrf_is_streaming(hackrf_device* device) { + return device->streaming; +} + +int hackrf_start_rx(hackrf_device* device, hackrf_sample_block_cb_fn callback) { + const uint8_t endpoint_address = LIBUSB_ENDPOINT_IN | 1; + int result = hackrf_set_transceiver_mode(device, HACKRF_TRANSCEIVER_MODE_RECEIVE); + if( result == HACKRF_SUCCESS ) { + create_transfer_thread(device, endpoint_address, callback); + } + return result; +} + +int hackrf_stop_rx(hackrf_device* device) { + return kill_transfer_thread(device); +} + +int hackrf_start_tx(hackrf_device* device, hackrf_sample_block_cb_fn callback) { + const uint8_t endpoint_address = LIBUSB_ENDPOINT_OUT | 2; + int result = hackrf_set_transceiver_mode(device, HACKRF_TRANSCEIVER_MODE_TRANSMIT); + if( result == HACKRF_SUCCESS ) { + result = create_transfer_thread(device, endpoint_address, callback); + } + return result; +} + +int hackrf_stop_tx(hackrf_device* device) { + return kill_transfer_thread(device); +} + +int hackrf_close(hackrf_device* device) { + if( device != NULL ) { + int result = hackrf_stop_rx(device); + if( result ) { + return result; + } + + result = hackrf_stop_tx(device); + if( result ) { + return result; + } + + if( device->usb_device != NULL ) { + result = libusb_release_interface(device->usb_device, 0); + if( result ) { + return HACKRF_ERROR_LIBUSB; + } + + libusb_close(device->usb_device); + + device->usb_device = NULL; + } + + free_transfers(device); + + free(device); + } + + return HACKRF_SUCCESS; +} + +const char* hackrf_error_name(enum hackrf_error errcode) { + switch(errcode) { + case HACKRF_SUCCESS: + return "HACKRF_SUCCESS"; + + case HACKRF_ERROR_INVALID_PARAM: + return "HACKRF_ERROR_INVALID_PARAM"; + + case HACKRF_ERROR_NOT_FOUND: + return "HACKRF_ERROR_NOT_FOUND"; + + case HACKRF_ERROR_BUSY: + return "HACKRF_ERROR_BUSY"; + + case HACKRF_ERROR_NO_MEM: + return "HACKRF_ERROR_NO_MEM"; + + case HACKRF_ERROR_LIBUSB: + return "HACKRF_ERROR_LIBUSB"; + + case HACKRF_ERROR_THREAD: + return "HACKRF_ERROR_THREAD"; + + case HACKRF_ERROR_OTHER: + return "HACKRF_ERROR_OTHER"; + + default: + return "HACKRF unknown error"; + } +} diff --git a/host/libhackrf/src/hackrf.h b/host/libhackrf/src/hackrf.h new file mode 100644 index 00000000..f06c6eec --- /dev/null +++ b/host/libhackrf/src/hackrf.h @@ -0,0 +1,71 @@ +/* + * Copyright 2012 Jared Boone + * + * This file is part of HackRF. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __HACKRF_H__ +#define __HACKRF_H__ + +#include +#include + +enum hackrf_error { + HACKRF_SUCCESS = 0, + HACKRF_ERROR_INVALID_PARAM = -2, + HACKRF_ERROR_NOT_FOUND = -5, + HACKRF_ERROR_BUSY = -6, + HACKRF_ERROR_NO_MEM = -11, + HACKRF_ERROR_LIBUSB = -1000, + HACKRF_ERROR_THREAD = -1001, + HACKRF_ERROR_OTHER = -9999, +}; + +typedef struct hackrf_device hackrf_device; + +typedef struct { + hackrf_device* device; + uint8_t* buffer; + int buffer_length; + int valid_length; +} hackrf_transfer; + +typedef int (*hackrf_sample_block_cb_fn)(hackrf_transfer* transfer); + +int hackrf_init(); +int hackrf_exit(); + +int hackrf_open(hackrf_device** device); +int hackrf_close(hackrf_device* device); + +//int hackrf_set_sampling_rate(float sampling_rate_hz); + +int hackrf_start_rx(hackrf_device* device, hackrf_sample_block_cb_fn callback); +int hackrf_stop_rx(hackrf_device* device); + +int hackrf_start_tx(hackrf_device* device, hackrf_sample_block_cb_fn callback); +int hackrf_stop_tx(hackrf_device* device); + +bool hackrf_is_streaming(hackrf_device* device); + +int hackrf_max2837_read(hackrf_device* device, uint8_t register_number, uint16_t* value); +int hackrf_max2837_write(hackrf_device* device, uint8_t register_number, uint16_t value); + +const char* hackrf_error_name(enum hackrf_error errcode); + +#endif//__HACKRF_H__