Fixes bug #916.
Previously, there was a race which could lead to a transfer being left
active after cancel_transfers() completed. This would then cause the
next prepare_transfers() call to fail, because libusb_submit_transfer()
would return an error due to the transfer already being in use.
The sequence of events that could cause this was:
1. Main thread calls hackrf_stop_rx(), which calls cancel_transfers(),
which iterates through the 4 transfers in use and cancels them one
by one with libusb_cancel_transfer().
2. During this time, a transfer is completed. The transfer thread calls
hackrf_libusb_transfer_callback(), which handles the data and then
calls libusb_submit_transfer() to resubmit that transfer.
3. Now, cancel_transfers() and hackrf_stop_rx() are completed but one
transfer is still active.
4. The next hackrf_start_rx() call fails, because prepare_transfers()
tries to submit a transfer which is already in use.
To fix this, we add a lock which must be held to either cancel transfers
or restart them. This ensures that only one of these actions can happen
for a given transfer; it's no longer possible for a transfer to be
cancelled and then immediately restarted.
APRS tracker which uses the HTML Geolocation API to retrieve the
device's position and WebUSB to communicate with the HackRF. The source
code is available at https://github.com/rgerganov/aprs-sdr
This is not currently essential, since the current M4 code will not
trigger an SGPIO interrupt until the offset and tx fields are set.
In future though, we want to explicitly set up the M0 state here.
One of the few instructions that can use the high registers (r8-r14) is
the add instruction, which can add any two registers, as long as one of
them is also used as the destination register.
By using this form of add , we can add buf_base (in a high register) to
the offset within the buffer (in a low register), to get the desired
pointer value (buf_ptr) which we want to access.
This saves one cycle by eliminating the need to move buf_base to a low
register first.
The lsr instruction here shifts the value in r0 right by one bit,
putting the LSB into the carry flag.
By setting the destination register to r1, we can retain the original
unshifted value in r0, and later write this to the INT_CLEAR register in
order to clear all bits that were set.
This saves two cycles by avoiding the need to load an 0xFFFF value to
write to INT_CLEAR.
The effect of the lsr instruction here is to shift the LSB of r0 into
the processor's carry flag. The subsequent bcc instruction ("branch if
carry clear") will then branch if this bit was zero.
The LSB corresponds to the exchange interrupt flag for slice A only. The
other interrupt flag bits are not checked here, contrary to the comment.
Keeping the base address of this structure in a register allows us to
use offsets to load individual fields from it, without needing their
individual addresses.
However, the ldr instruction can only use immediate offsets relative to
the low registers (r0-r7), or the stack pointer (r13).
Low registers are in short supply and are needed for other instructions
which can only use r0-r7, so we use the stack pointer here.
It's safe to do this because we do not use the stack. There are no
function calls, interrupt handlers or push/pop instructions in the M0
code.
This change saves four cycles by eliminating loads of the addresses for
the offset & tx registers, plus a further two by eliminating the need to
stash one of these addresses in r8.
The high registers (r8-r14) cannot be used directly by most of the
instructions in the Cortex-M0 instruction set.
One of the few instructions that can use them is mov, which can use
any pair of registers.
This allows saving two cycles, by replacing two loads (2 cycles each)
with moves (1 cycle each), after stashing the required values in high
registers at startup.
This allows us to use ldr/str with an immediate offset to access the
SGPIO interrupt registers, rather than first having to load a register
with the specific address we want to access.
This change saves a total of 6 cycles, by eliminating two loads (2
cycles each), one of which could be executed twice.
The current code does reads and writes in two chunks: one of
6 words, followed by one of 2.
Instead, use two chunks of 4 words each. This takes the same number of
total cycles, but frees up two registers for other uses.
Note that we can't do things in one chunk, because we'd need eight
registers to hold the data, plus a ninth to hold the buffer pointer. The
ldm/stm instructions can only use the eight low registers, r0-r7.
So we have to use two chunks, and the most register-efficient way to do
that is to use two equal chunks.
Previously this register was reloaded with the same value during each loop.
Initialising it once, outside the loop, saves two cycles.
Note the separation of the loop start ("loop") from the entry point ("main").
Code between these labels will be run once, at startup.
These variables are already placed together; this commit just groups
them into a struct and declares this in a new header.
This commit should not result in any change to the firmware binary.
Only the names of symbols are changed.
This removes the definition of the offset variable,
volatile uint32_t usb_bulk_buffer_offset = 0;
which is actually superfluous. This variable, along with its neighbour
usb_bulk_buffer_tx, is placed explicitly by the linker script. Its type
is defined by the declaration in usb_bulk_buffer.h.
There is no need to define it here, and doing so gives the misleading
impression that its initial value can be changed by modifying this line!
The initialization to zero never actually takes effect, because the
variable is not placed in the .data or .bss sections which are
initialised by the startup code.
The offset and tx variables are both set in set_transceiver_mode
before SGPIO streaming is started, so the M0 code does not use
them uninitialised.
When switching to frequency mode or modifying the frequency ranges,
ensure that the Opera Cake is switched to the correct port on the very
next tuning.
* Clean up the CMake build system and improve the FindFFTW3 module.
* Fixes for Linux build
* Include winsock.h to get struct timeval
* Couple more fixes for MSVC, also add new FindMath module
* Update host build README for new CMake changes (esp. Windows)
* Try to fix Travis OS X build error
* Add docs about pthread-win32
* Whoops, AppVeyor caught a bug in FindFFTW where the includes not being found weren't generating a fatal error.
* Travis rebuild bump
* One more fix: replace hardcoded include paths with a PATH_SUFFIX to standard include paths
* Invert Windows preprocessor flag so it's only needed when using a static build. This preserves compatibility with the previous system.
* Fix copy-paste error
* Update cmake modules from amber-cmake upstream, incorporate TryLinkLibrary into FindUSB1
* Fix missing include