Skip to content

Commit

Permalink
Fix delayed header in a capture
Browse files Browse the repository at this point in the history
When a client connects to the data server, the header will not appear
after the capture is enabled until there is some sample data.

We fix that by releasing the buffer once a valid timestamp is captured.

This fixes #49.
  • Loading branch information
EmilioPeJu committed Mar 4, 2024
1 parent d92547c commit f988327
Show file tree
Hide file tree
Showing 5 changed files with 65 additions and 28 deletions.
29 changes: 24 additions & 5 deletions driver/panda_stream.c
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,8 @@ struct stream_open {
bool stream_active; // If not set, any interrupts are unexpected
uint32_t completion; // Copy of final interrupt status register
struct timespec64 start_ts; // timestamp when PCAP becomes armed & enabled
bool start_ts_valid; // whether start_ts was set
spinlock_t start_ts_lock; // it handles race between isr and ioctl

/* Reader status. */
int read_block_index; // Block being read
Expand Down Expand Up @@ -201,8 +203,13 @@ static irqreturn_t stream_isr(int irq, void *dev_id)

if (open->stream_active)
{
if (IRQ_STATUS_START_EVENT(status))
if (IRQ_STATUS_START_EVENT(status)) {
unsigned long flags;
spin_lock_irqsave(&open->start_ts_lock, flags);
ktime_get_real_ts64(&open->start_ts);
open->start_ts_valid = true;
spin_unlock_irqrestore(&open->start_ts_lock, flags);
}

if (IRQ_STATUS_NEW_BUFFER(status))
{
Expand Down Expand Up @@ -297,8 +304,9 @@ static void start_hardware(struct stream_open *open)
open->isr_block_index = 0;
open->read_block_index = 0;
open->read_offset = 0;
/* having a zeroed timestamp means the start event did not happen. */
open->start_ts = (const struct timespec64){0};
open->start_ts_valid = false;
spin_lock_init(&open->start_ts_lock);
/* After this point we can allow interrupts, they can potentially start as
* soon as a DMA buffer is assigned. */
smp_wmb();
Expand Down Expand Up @@ -515,9 +523,20 @@ static long panda_stream_ioctl(
case PANDA_COMPLETION:
return stream_completion(open, (void __user *) arg);
case PANDA_GET_START_TS:
return copy_to_user(
(void __user *) arg, &open->start_ts,
sizeof(struct timespec64)) ? -EIO : 0;
{
struct timespec64 ts;
bool ts_valid;
spin_lock(&open->start_ts_lock);
ts = open->start_ts;
ts_valid = open->start_ts_valid;
spin_unlock(&open->start_ts_lock);
if (!ts_valid)
return -EAGAIN;

return copy_to_user(
(void __user *) arg, &ts,
sizeof(struct timespec64)) ? -EIO : 0;
}
default:
return -EINVAL;
}
Expand Down
30 changes: 17 additions & 13 deletions server/data_server.c
Original file line number Diff line number Diff line change
Expand Up @@ -113,10 +113,13 @@ static uint64_t experiment_sample_count;
/* Save start timestamp, either from hardware (if that timestamp is not 0), or
* from the driver (which was saved in the interrupt handler triggered by the
* start event) */
static void update_start_timestamp(void)
static bool update_start_timestamp(void)
{
struct timespec pcap_drv_start_ts, pcap_hw_start_ts;
hw_get_start_ts(&pcap_drv_start_ts);
bool valid_ts = hw_get_start_ts(&pcap_drv_start_ts);
if (!valid_ts)
return false;

hw_get_hw_start_ts(&pcap_hw_start_ts);
if (pcap_hw_start_ts.tv_sec == 0 && pcap_hw_start_ts.tv_nsec == 0)
{
Expand All @@ -133,6 +136,8 @@ static void update_start_timestamp(void)
pcap_hw_ts_offset_ns = drv_ts_num - hw_ts_num;
pcap_hw_ts_offset_ns_valid = true;
}

return true;
}


Expand All @@ -154,18 +159,17 @@ static void capture_experiment(void)
{
void *block = get_write_block(data_buffer);
size_t count;
do
do {
count = hw_read_streamed_data(block, DATA_BLOCK_SIZE, &at_eof);
while (data_thread_running && count == 0 && !at_eof);

/* Do our best to capture a timestamp before releasing the first block
* of the experiment. If the experiment is empty this return an empty
* timestamp, that's how it goes. */
if (!ts_captured)
{
update_start_timestamp();
ts_captured = true;
}
if (!ts_captured)
{
ts_captured = update_start_timestamp();
/* we want to release the block when we get the timestamp to
* notify the readers */
if (ts_captured)
break;
}
} while (data_thread_running && count == 0 && !at_eof);

/* Unconditionally release the write block here. Either we have data to
* send (exited loop above with count==0), or we're at the end of the
Expand Down
24 changes: 18 additions & 6 deletions server/hardware.c
Original file line number Diff line number Diff line change
Expand Up @@ -316,22 +316,34 @@ unsigned int hw_read_streamed_completion(void)
}


void hw_get_start_ts(struct timespec *ts)
bool hw_get_start_ts(struct timespec *ts)
{
struct timespec64 compat_ts = (struct timespec64) {0};
error_report(TEST_IO(ioctl(stream, PANDA_GET_START_TS, &compat_ts)));
ts->tv_sec = (time_t) compat_ts.tv_sec;
ts->tv_nsec = (typeof(ts->tv_nsec)) compat_ts.tv_nsec;
struct timespec64 compat_ts = {};
if (ioctl(stream, PANDA_GET_START_TS, &compat_ts) == -1)
{
// EAGAIN indicates the timestamp hasn't been captured yet
if (errno != EAGAIN)
error_report(TEST_IO(-1));

return false;
}
else
{
ts->tv_sec = (time_t) compat_ts.tv_sec;
ts->tv_nsec = (typeof(ts->tv_nsec)) compat_ts.tv_nsec;
return true;
}
}


void hw_get_hw_start_ts(struct timespec *ts)
bool hw_get_hw_start_ts(struct timespec *ts)
{
ts->tv_sec = (time_t) read_named_register(PCAP_TS_SEC);
ts->tv_nsec = (typeof(ts->tv_nsec)) ((uint64_t)
read_named_register(PCAP_TS_TICKS) * NSECS / hw_read_nominal_clock());
ts->tv_sec += ts->tv_nsec / NSECS;
ts->tv_nsec = ts->tv_nsec % NSECS;
return true;
}

#endif
Expand Down
4 changes: 2 additions & 2 deletions server/hardware.h
Original file line number Diff line number Diff line change
Expand Up @@ -165,9 +165,9 @@ unsigned int hw_read_streamed_completion(void);
const char *hw_decode_completion(unsigned int completion);

/* This function gets the timestamp when PCAP becomes armed and enabled */
void hw_get_start_ts(struct timespec *ts);
bool hw_get_start_ts(struct timespec *ts);
/* This one is latched in hardware instead of the driver */
void hw_get_hw_start_ts(struct timespec *ts);
bool hw_get_hw_start_ts(struct timespec *ts);

/* This function controls the arm/disarm state of data capture. Data capture is
* armed by writing true with this function, after which hw_read_streamed_data()
Expand Down
6 changes: 4 additions & 2 deletions server/sim_hardware.c
Original file line number Diff line number Diff line change
Expand Up @@ -182,16 +182,18 @@ void hw_write_arm_streamed_data(void) { }
uint32_t hw_read_streamed_completion(void) { return 0; }


void hw_get_start_ts(struct timespec *ts)
bool hw_get_start_ts(struct timespec *ts)
{
clock_gettime(CLOCK_REALTIME, ts);
return true;
}


void hw_get_hw_start_ts(struct timespec *ts)
bool hw_get_hw_start_ts(struct timespec *ts)
{
ts->tv_sec = 0;
ts->tv_nsec = 0;
return true;
}


Expand Down

0 comments on commit f988327

Please sign in to comment.