Commit ba98b0cf authored by Andrey Filippov's avatar Andrey Filippov

working version with 2 parallel write threads

parent bff5f99c
......@@ -37,6 +37,7 @@
#include "camogm_read.h"
#include "camogm.h"
#include "camogm_align.h" // for PHY_BLOCK_SIZE=512
#ifdef USE_POLL
#include <poll.h>
......@@ -241,6 +242,7 @@ void camogm_init(camogm_state *state, char *pipe_name, uint16_t port_num)
{
const char sserial[] = "elp0";
int * ipser = (int*)sserial;
int i;
memset(state, 0, sizeof(camogm_state));
camogm_set_segment_duration(state, DEFAULT_DURATION);
......@@ -281,11 +283,16 @@ void camogm_init(camogm_state *state, char *pipe_name, uint16_t port_num)
state->rawdev.mmap_default_size = MMAP_CHUNK_SIZE; // only used for camogm_read
state->sock_port = port_num;
// now repeated in start
state->writer_params.chunk_page_prep = 0; // will prepare page 0
state->writer_params.chunk_page_write = 0; // will write page 0
state->writer_params.writev_run = false;
for (i=0; i < SEGMENTS_PAGES; i++){
state->writer_params.chunk_page_state[i] = SEGPAGE_EMPTY;
}
for (i=0; i < NUM_WRITER_THREADS; i++){
state->writer_params.writev_run[i] = false;
state->writer_params.write_waits_sig[i] = false;
}
state->writer_params.exit_thread = false;
state->writer_params.state = STATE_STOPPED;
camogm_set_dummy_read(state, 0);
......@@ -338,6 +345,7 @@ int camogm_start(camogm_state *state)
int dummy;
int next_metadata_start, next_jpeg_len, fp;
int port = state->port_num;
int i, used_space;
if (state->active_chn == ALL_CHN_INACTIVE) {
D0(fprintf(debug_file, "All channels are disabled, will not start\n"));
......@@ -379,6 +387,10 @@ int camogm_start(camogm_state *state)
pthread_mutex_lock(&state->mutex);
state->prog_state = STATE_STARTING;
pthread_mutex_unlock(&state->mutex);
D6(fprintf(debug_file, "Starting recording-1\n"));
#ifdef MIN_USED_SIZE
state->greedy = 1;
#endif
FOR_EACH_PORT(int, chn) {
if (is_chn_active(state, chn)) {
// Check/set circbuf read pointer
......@@ -386,25 +398,52 @@ int camogm_start(camogm_state *state)
D3(fprintf(debug_file, "1a: compressed frame number = %li\n", lseek(state->fd_circ[chn], LSEEK_CIRC_GETFRAME, SEEK_END)));
if ((state->cirbuf_rp[chn] < 0) || (lseek(state->fd_circ[chn], state->cirbuf_rp[chn], SEEK_SET) < 0) || (lseek(state->fd_circ[chn], LSEEK_CIRC_VALID, SEEK_END) < 0 )) {
D3(fprintf(debug_file, "2: state->cirbuf_rp=0x%x\n", state->cirbuf_rp[chn]));
D3(fprintf(debug_file, "2.1: state->greedy=%d\n", state->greedy));
/* In "greedy" mode try to save as many frames from the circbuf as possible */
state->cirbuf_rp[chn] = lseek(state->fd_circ[chn], state->greedy ? LSEEK_CIRC_SCND : LSEEK_CIRC_LAST, SEEK_END);
// state->cirbuf_rp[chn] = lseek(state->fd_circ[chn], state->greedy ? LSEEK_CIRC_SCND : LSEEK_CIRC_LAST, SEEK_END);
state->cirbuf_rp[chn] = lseek(state->fd_circ[chn], LSEEK_CIRC_LAST, SEEK_END);
D3(fprintf(debug_file, "2.1: last, fp=0x%x\n", state->cirbuf_rp[chn]));
D3(fprintf(debug_file, "2.12: same %d, fp=0x%lx\n", i, lseek(state->fd_circ[chn], state->cirbuf_rp[chn], SEEK_SET)));
for (i = 0; i < 100; i++){
state->cirbuf_rp[chn]=lseek(state->fd_circ[chn], LSEEK_CIRC_PREV, SEEK_END);
used_space= lseek(state->fd_circ[chn], LSEEK_CIRC_USED, SEEK_END);
lseek(state->fd_circ[chn], state->cirbuf_rp[chn], SEEK_SET); // restore
if (used_space >= MIN_USED_SIZE) {
D3(fprintf(debug_file, "2.16: %d used (%d) >= needed (%d)\n", i, used_space, MIN_USED_SIZE ));
break;
}
}
lseek(state->fd_circ[chn], state->cirbuf_rp[chn], SEEK_SET); // just in case - restore pointer
if (!state->ignore_fps) { // don't even try in ignore mode
if (((fp = lseek(state->fd_circ[chn], LSEEK_CIRC_PREV, SEEK_END))) >= 0) state->cirbuf_rp[chn] = fp;//try to have 2 frames available for fps
}
state->buf_overruns[chn]++;
// file pointer here should match state->rp; so no need to do lseek(state->fd_circ,state->cirbuf_rp,SEEK_SET);
state->buf_min[chn] = getGPValue(chn, G_FREECIRCBUF);
} else {
if (state->buf_min[chn] > getGPValue(chn, G_FREECIRCBUF)) state->buf_min[chn] = getGPValue(chn, G_FREECIRCBUF);
}
D3(fprintf(debug_file, "3: state->cirbuf_rp=0x%x\n", state->cirbuf_rp[chn]));
D3(fprintf(debug_file, "4:lseek(state->fd_circ,LSEEK_CIRC_READY,SEEK_END)=%d\n", (int)lseek(state->fd_circ[chn], LSEEK_CIRC_READY, SEEK_END)));
D3(fprintf(debug_file, "4:lseek(state->fd_circ,LSEEK_CIRC_READY,SEEK_END)=0x%x\n",\
(int)lseek(state->fd_circ[chn], LSEEK_CIRC_READY, SEEK_END)));
// is this frame ready?
if (lseek(state->fd_circ[chn], LSEEK_CIRC_READY, SEEK_END) < 0) return -CAMOGM_FRAME_NOT_READY; // frame pointer valid, but no frames yet
if (lseek(state->fd_circ[chn], LSEEK_CIRC_READY, SEEK_END) < 0) {
D2(fprintf(debug_file, "Starting recording-10 CAMOGM_FRAME_NOT_READY\n"));
return -CAMOGM_FRAME_NOT_READY; // frame pointer valid, but no frames yet
}
#ifdef MIN_USED_SIZE
if (MIN_USED_SIZE > 0) {
if ((fp=lseek(state->fd_circ[chn], LSEEK_CIRC_USED, SEEK_END)) < MIN_USED_SIZE) {
D2(fprintf(debug_file, "Starting recording-10A, TOO FEW FRAMES READY fp=0x%x\n", fp)); //frame not ready, frame pointer seems valid, but not ready
lseek(state->fd_circ[chn], state->cirbuf_rp[chn], SEEK_SET);
return -CAMOGM_FRAME_NOT_READY; // frame pointer valid, but no frames yet
}
fp = lseek(state->fd_circ[chn], state->cirbuf_rp[chn], SEEK_SET); //restore
}
#endif
D3(fprintf(debug_file, "5: state->cirbuf_rp=0x%x\n", state->cirbuf_rp[chn]));
state->metadata_start = (state->cirbuf_rp[chn]) - 32;
if (state->metadata_start < 0) state->metadata_start += state->circ_buff_size[chn];
......@@ -420,6 +459,7 @@ int camogm_start(camogm_state *state)
ifp = (int *) &state->frame_params[chn];
D1(fprintf(debug_file, "state->cirbuf_rp=0x%x\r\n", (int)state->cirbuf_rp[chn]));
D1(fprintf(debug_file, "%08x %08x %08x %08x %08x %08x %08x %08x\r\n", ifp[0], ifp[1], ifp[2], ifp[3], ifp[4], ifp[5], ifp[6], ifp[7]));
D2(fprintf(debug_file, "Starting recording-11 CAMOGM_FRAME_BROKEN\n"));
return -CAMOGM_FRAME_BROKEN;
}
// find location of the timestamp and copy it to the frame_params structure
......@@ -433,6 +473,7 @@ int camogm_start(camogm_state *state)
if (dtime_stamp < state->start_after_timestamp) {
state->cirbuf_rp[chn] = -1;
D3(fprintf(debug_file, "Too early to start, %f < %f\n", dtime_stamp, state->start_after_timestamp));
D2(fprintf(debug_file, "Starting recording-12 CAMOGM_TOO_EARLY\n"));
return -CAMOGM_TOO_EARLY;
}
}
......@@ -444,6 +485,7 @@ int camogm_start(camogm_state *state)
D3(fprintf(debug_file, "6a:lseek(state->fd_circ,LSEEK_CIRC_NEXT,SEEK_END)=0x%x, fp=0x%x\n", (int)lseek(state->fd_circ[chn], LSEEK_CIRC_NEXT, SEEK_END), (int)lseek(state->fd_circ[chn], LSEEK_CIRC_READY, SEEK_END)));
lseek(state->fd_circ[chn], state->cirbuf_rp[chn], SEEK_SET); // just in case - restore pointer
D2(fprintf(debug_file, "Starting recording-13 CAMOGM_FRAME_NOT_READY\n"));
return -CAMOGM_FRAME_NOT_READY; // frame pointer valid, but no frames yet
}
next_metadata_start = fp - 32;
......@@ -456,6 +498,7 @@ int camogm_start(camogm_state *state)
D1(fprintf(debug_file, "fp=0x%x\r\n", (int)fp));
D1(fprintf(debug_file, "%08x %08x %08x %08x %08x %08x %08x %08x\r\n", ifp_this[0], ifp_this[1], ifp_this[2], ifp_this[3], ifp_this[4], ifp_this[5], ifp_this[6], ifp_this[7]));
state->cirbuf_rp[chn] = -1;
D2(fprintf(debug_file, "Starting recording-14 CAMOGM_FRAME_BROKEN\n"));
return -CAMOGM_FRAME_BROKEN;
}
D3(fprintf(debug_file, "7: state->cirbuf_rp=0x%x\n", state->cirbuf_rp[chn]));
......@@ -469,6 +512,7 @@ int camogm_start(camogm_state *state)
(state->frame_params[chn].height != state->this_frame_params[chn].height))) {
// Advance frame pointer to the next (caller should try again)
state->cirbuf_rp[chn] = fp;
D2(fprintf(debug_file, "Starting recording-15 CAMOGM_FRAME_CHANGED\n"));
return -CAMOGM_FRAME_CHANGED; // no yet checking for the FPS
}
D3(fprintf(debug_file, "8: state->cirbuf_rp=0x%x\n", state->cirbuf_rp[chn]));
......@@ -498,6 +542,7 @@ int camogm_start(camogm_state *state)
state->head_size[chn] = lseek(state->fd_head[chn], 0, SEEK_END); // In 8.0 the header size might change for some jp4 modes
if (state->head_size[chn] > JPEG_HEADER_MAXSIZE) {
D0(fprintf(debug_file, "%s:%d: Too big JPEG header (%d > %d)", __FILE__, __LINE__, state->head_size[chn], JPEG_HEADER_MAXSIZE ));
D2(fprintf(debug_file, "Starting recording-16 -2\n"));
return -2;
}
// and read it
......@@ -508,6 +553,8 @@ int camogm_start(camogm_state *state)
state->image_size[chn] = 0;
}
}
D2(fprintf(debug_file, "Starting recording-2, circbuf[0]=0x%08x, circbuf[1]=0x%08x, circbuf[2]=0x%08x, circbuf[3]=0x%08x\n", \
(int)ccam_dma_buf[0], (int)ccam_dma_buf[1], (int)ccam_dma_buf[2], (int)ccam_dma_buf[3]));
// here we are ready to initialize Ogm (or other) file
switch (state->format) {
......@@ -517,11 +564,14 @@ int camogm_start(camogm_state *state)
case CAMOGM_FORMAT_MOV: rslt = camogm_start_mov(state); break;
default: rslt = 0; // do nothing
}
D6(fprintf(debug_file, "Starting recording-3\n"));
if (rslt) {
unsigned int err_code = -rslt;
D0(fprintf(debug_file, "camogm_start() error, rslt=0x%x\n", rslt));
if (err_code > 0 && err_code < CAMOGM_ERRNUM)
state->error_stat[port][err_code]++;
if ((err_code > 0) && (err_code < CAMOGM_ERRNUM) && (port >= 0) && (port < SENSOR_PORTS)){
state->error_stat[port][err_code]++; // is it second time increment?
}
return rslt;
}
if (state->kml_enable) rslt = camogm_start_kml(state); // will turn on state->kml_used if it can
......@@ -530,6 +580,7 @@ int camogm_start(camogm_state *state)
state->prog_state = STATE_RUNNING;
pthread_mutex_unlock(&state->mutex);
D1(fprintf(debug_file, "Started OK\n"));
return 0;
}
......@@ -545,64 +596,82 @@ int sendImageFrame(camogm_state *state)
int timestamp_start;
int * ifp_this = (int*)&(state->this_frame_params[state->port_num]);
int fp;
int fd_exif_tiff; // switching between TIFF and Exif depending on coloe mode
int port = state->port_num;
struct timeval start_time, end_time;
if (port < 0) {
D3(fprintf(debug_file, "sendImageFrame:0: port < 0!\n"));
return -CAMOGM_FRAME_BROKEN;
}
// start_time = get_fpga_time(state->fd_fparmsall[port], port);
// This is probably needed only for Quicktime (not to exceed already allocated frame index)
int fd_exif_tiff; // switching between TIFF and Exif depending on coloe mode
int port = state->port_num;
struct timeval start_time, end_time;
int i,j,k; //debug
int dbg_meta;
if (port < 0) {
D1(fprintf(debug_file, "sendImageFrame:0: port < 0!\n"));
return -CAMOGM_FRAME_BROKEN;
}
// start_time = get_fpga_time(state->fd_fparmsall[port], port);
// This is probably needed only for Quicktime (not to exceed already allocated frame index)
if (!state->rawdev_op && (state->frameno >= (state->max_frames))) {
D3(fprintf(debug_file, "sendImageFrame:1: state->frameno(0x%x) >= state->max_frames(0x%x)\n", state->frameno, state->max_frames));
return -CAMOGM_FRAME_NEXTFILE;
}
// Format changed?
// D3(fprintf (debug_file,"sendImageFrame: format=%d, set_format=%d\n", state->format, state->set_format));
// Format changed?
// D3(fprintf (debug_file,"sendImageFrame: format=%d, set_format=%d\n", state->format, state->set_format));
if (state->format != state->set_format) {
D3(fprintf(debug_file, "sendImageFrame:2: state->format(0x%x) != state->set_format(0x%x)\n", state->format, state->set_format));
D2(fprintf(debug_file, "sendImageFrame:2: state->format(0x%x) != state->set_format(0x%x)\n", state->format, state->set_format));
return -CAMOGM_FRAME_CHANGED;
}
// check if file size is exceeded (assuming fopen),-CAMOGM_FRAME_CHANGED will trigger a new segment
// check if file size is exceeded (assuming fopen),-CAMOGM_FRAME_CHANGED will trigger a new segment
if (!state->rawdev_op && (state->vf) && (state->segment_length >= 0) && (ftell(state->vf) > state->segment_length)) {
D3(fprintf(debug_file, "sendImageFrame:3: segment length exceeded\n"));
D2(fprintf(debug_file, "sendImageFrame:3: segment length exceeded\n"));
return -CAMOGM_FRAME_NEXTFILE;
}
//same for open
//same for open
if (!state->rawdev_op && ((state->ivf) >= 0) && (state->segment_length >= 0) && (lseek(state->ivf, 0, SEEK_CUR) > state->segment_length)) {
D3(fprintf(debug_file, "sendImageFrame:4: segment length exceeded\n"));
return -CAMOGM_FRAME_NEXTFILE;
}
// check the frame pointer is valid
// check the frame pointer is valid
if ((fp = lseek(state->fd_circ[port], state->cirbuf_rp[port], SEEK_SET)) < 0) {
D3(fprintf(debug_file, "sendImageFrame:5: invalid frame\n"));
D2(fprintf(debug_file, "sendImageFrame:5: invalid frame\n"));
return -CAMOGM_FRAME_INVALID; //it will probably be that allready
}
// is the frame ready?
// is the frame ready?
if (lseek(state->fd_circ[port], LSEEK_CIRC_READY, SEEK_END) < 0) {
D4(fprintf(debug_file, "?6")) ; //frame not ready, frame pointer seems valid, but not ready
D6(fprintf(debug_file, ",fp=0x%x\n", fp)); //frame not ready, frame pointer seems valid, but not ready
D3(fprintf(debug_file, "?6")) ; //frame not ready, frame pointer seems valid, but not ready
D6(fprintf(debug_file, ",FRAME NOT READY fp=0x%x\n", fp)); //frame not ready, frame pointer seems valid, but not ready
return -CAMOGM_FRAME_NOT_READY; // frame pointer valid, but no frames yet
}
// process skipping frames. TODO: add - skipping time between frames (or better - actual time period - use the nearest frame) instead of the frame number
#ifdef MIN_USED_SIZE
if (MIN_USED_SIZE > 0) {
if (lseek(state->fd_circ[port], LSEEK_CIRC_USED, SEEK_END) < MIN_USED_SIZE) {
D6(fprintf(debug_file, ", TOO FEW FRAMES READY fp=0x%x\n", fp)); //frame not ready, frame pointer seems valid, but not ready
lseek(state->fd_circ[port], state->cirbuf_rp[port], SEEK_SET); // just in case - restore pointer
return -CAMOGM_FRAME_NOT_READY; // frame pointer valid, but no frames yet
}
lseek(state->fd_circ[port], state->cirbuf_rp[port], SEEK_SET); // restore pointer, needed!
}
#endif
// process skipping frames. TODO: add - skipping time between frames (or better - actual time period - use the nearest frame) instead of the frame number
if ( (state->frames_skip > 0) && (state->frames_skip_left[port] > 0 )) { //skipping frames, not seconds.
state->cirbuf_rp[port] = lseek(state->fd_circ[port], LSEEK_CIRC_NEXT, SEEK_END);
//optionally save it to global read pointer (i.e. for debugging with imgsrv "/pointers")
//optionally save it to global read pointer (i.e. for debugging with imgsrv "/pointers")
if (state->save_gp) lseek(state->fd_circ[port], LSEEK_CIRC_SETP, SEEK_END);
state->frames_skip_left[port]--;
D3(fprintf(debug_file, "?7\n")); //frame not ready
D2(fprintf(debug_file, "?7\n")); //frame not ready
return -CAMOGM_FRAME_NOT_READY; // the required frame is not ready
}
// Get metadata
// Get metadata
D4(fprintf(debug_file, "_1_"));
D1(fprintf(debug_file, " state->cirbuf_rp[%d] = 0x%08x ", port, state->cirbuf_rp[port]));
state->metadata_start = state->cirbuf_rp[port] - 32;
if (state->metadata_start < 0) state->metadata_start += state->circ_buff_size[port];
memcpy(&(state->this_frame_params[port]), (unsigned long* )&ccam_dma_buf[state->port_num][state->metadata_start >> 2], 32);
// twice metadata
D6(fprintf(debug_file,"\n_1a_ port=%d, state->cirbuf_rp[port]=0x%08x\n",port,state->cirbuf_rp[port]); for (i=0; i< 16; i++) { \
fprintf(debug_file," 0x%08lx", ((unsigned long* )&ccam_dma_buf[port][state->metadata_start >> 2])[i]); \
if ((i & 7) == 7) fprintf(debug_file,"\n"); });
state->jpeg_len = state->this_frame_params[port].frame_length; // frame_params.frame_length are now the length of bitstream
if (state->this_frame_params[port].signffff != 0xffff) {
D0(fprintf(debug_file, "%s:%d: wrong signature - %d\r\n", __FILE__, __LINE__, (int)state->this_frame_params[port].signffff));
......@@ -613,29 +682,38 @@ int sendImageFrame(camogm_state *state)
return -CAMOGM_FRAME_BROKEN;
}
D4(fprintf(debug_file, "_2_"));
// find location of the timestamp and copy it to the frame_params structure
// find location of the timestamp and copy it to the frame_params structure
timestamp_start = state->cirbuf_rp[port] + ((state->jpeg_len + CCAM_MMAP_META + 3) & (~0x1f)) + 32 - CCAM_MMAP_META_SEC; // magic shift - should index first byte of the time stamp
if (timestamp_start >= state->circ_buff_size[port]) timestamp_start -= state->circ_buff_size[port];
D4(fprintf(debug_file, "_3_"));
memcpy(&(state->this_frame_params[port].timestamp_sec), (unsigned long* )&ccam_dma_buf[state->port_num][timestamp_start >> 2], 8);
// verify that the essential current frame params did not change, if they did - return an error (need new file header)
// verify that the essential current frame params did not change, if they did - return an error (need new file header)
if (!state->ignore_fps && ((state->frame_params[port].width != state->this_frame_params[port].width) ||
(state->frame_params[port].height != state->this_frame_params[port].height))) {
D3(fprintf(debug_file, "sendImageFrame:9: WOI changed\n"));
return -CAMOGM_FRAME_CHANGED; // not yet checking for the FPS
(state->frame_params[port].height != state->this_frame_params[port].height))) {
D2(fprintf(debug_file, "sendImageFrame:9: WOI changed: ignore_fps=%d, width=%d(%d), height=%d(%d)\n", \
state->ignore_fps, state->this_frame_params[port].width, state->frame_params[port].width, \
state->this_frame_params[port].height, state->frame_params[port].height));
memcpy(&(state->this_frame_params[port].timestamp_sec), (unsigned long* )&ccam_dma_buf[state->port_num][timestamp_start >> 2], 8);
D2(fprintf(debug_file, "sendImageFrame:9: WOI changed: ignore_fps=%d, width=%d(%d), height=%d(%d)\n", \
state->ignore_fps, state->this_frame_params[port].width, state->frame_params[port].width, \
state->this_frame_params[port].height, state->frame_params[port].height));
if (!state->ignore_fps && ((state->frame_params[port].width != state->this_frame_params[port].width) ||
(state->frame_params[port].height != state->this_frame_params[port].height))) {
return -CAMOGM_FRAME_CHANGED; // not yet checking for the FPS
}
}
// check if file duration (in seconds) exceeded ,-CAMOGM_FRAME_CHANGED will trigger a new segment
// check if file duration (in seconds) exceeded ,-CAMOGM_FRAME_CHANGED will trigger a new segment
if (!state->rawdev_op && (state->segment_duration > 0) &&
((state->this_frame_params[port].timestamp_sec - state->frame_params[port].timestamp_sec) > state->segment_duration)) {
D3(fprintf(debug_file, "sendImageFrame:10: segment duration in seconds exceeded\n"));
((state->this_frame_params[port].timestamp_sec - state->frame_params[port].timestamp_sec) > state->segment_duration)) {
D1(fprintf(debug_file, "sendImageFrame:10: segment duration in seconds exceeded\n"));
return -CAMOGM_FRAME_CHANGED;
}
// check if (in timelapse mode) it is too early for the frame to be stored
// check if (in timelapse mode) it is too early for the frame to be stored
if ((state->frames_skip < 0) && (state->frames_skip_left[port] > state->this_frame_params[port].timestamp_sec) ) {
state->cirbuf_rp[port] = lseek(state->fd_circ[port], LSEEK_CIRC_NEXT, SEEK_END);
//optionally save it to global read pointer (i.e. for debugging with imgsrv "/pointers")
//optionally save it to global read pointer (i.e. for debugging with imgsrv "/pointers")
if (state->save_gp) lseek(state->fd_circ[port], LSEEK_CIRC_SETP, SEEK_END);
D3(fprintf(debug_file, "sendImageFrame:11: timelapse: frame will be skipped\n"));
D1(fprintf(debug_file, "sendImageFrame:11: timelapse: frame will be skipped\n"));
return -CAMOGM_FRAME_NOT_READY; // the required frame is not ready
}
......@@ -644,10 +722,10 @@ int sendImageFrame(camogm_state *state)
if (state->exif) {
D4(fprintf(debug_file, "_5_"));
fd_exif_tiff = (state->this_frame_params[port].color == COLORMODE_RAW)? state->fd_tiff[port] : state->fd_exif[port];
// update the Exif header with the current frame metadata
// update the Exif header with the current frame metadata
state->exifSize[port] = lseek(fd_exif_tiff, 1, SEEK_END); // at the beginning of page 1 - position == page length
if (state->exifSize[port] > 0) {
//state->this_frame_params.meta_index
//state->this_frame_params.meta_index
lseek(fd_exif_tiff, state->this_frame_params[port].meta_index, SEEK_END); // select meta page to use (matching frame)
rslt = read(fd_exif_tiff, state->ed[port], state->exifSize[port]);
if (rslt < 0) rslt = 0;
......@@ -657,7 +735,7 @@ int sendImageFrame(camogm_state *state)
D4(fprintf(debug_file, "_6_"));
// prepare a packet to be sent (a lst of memory chunks)
// prepare a packet to be sent (a lst of memory chunks)
state->chunk_index = 0;
state->packetchunks[state->chunk_index ].bytes = 1;
state->packetchunks[state->chunk_index++].chunk = &frame_packet_type; // [0]
......@@ -684,20 +762,20 @@ int sendImageFrame(camogm_state *state)
}
D4(fprintf(debug_file, "_9_"));
/* Jpeg/Tiff image data may be split in two segments (rolled over buffer end) - process both variants */
/* Jpeg/Tiff image data may be split in two segments (rolled over buffer end) - process both variants */
state->chunk_data_index = state->chunk_index; // index of the first data segment
if ((state->cirbuf_rp[port] + state->jpeg_len) > state->circ_buff_size[port]) { // two segments
/* copy from the beginning of the frame to the end of the buffer */
/* copy from the beginning of the frame to the end of the buffer */
D4(fprintf(debug_file, "_10_"));
state->packetchunks[state->chunk_index ].bytes = state->circ_buff_size[port] - state->cirbuf_rp[port];
state->packetchunks[state->chunk_index++].chunk = (unsigned char*)&ccam_dma_buf[state->port_num][state->cirbuf_rp[port] >> 2];
/* copy from the beginning of the buffer to the end of the frame */
/* copy from the beginning of the buffer to the end of the frame */
state->packetchunks[state->chunk_index ].bytes = state->jpeg_len - (state->circ_buff_size[port] - state->cirbuf_rp[port]);
state->packetchunks[state->chunk_index++].chunk = (unsigned char*)&ccam_dma_buf[state->port_num][0];
state->data_segments = 2;
} else { // single segment
D4(fprintf(debug_file, "_11_"));
/* copy from the beginning of the frame to the end of the frame (no buffer rollovers) */
/* copy from the beginning of the frame to the end of the frame (no buffer rollovers) */
state->packetchunks[state->chunk_index ].bytes = state->jpeg_len;
state->packetchunks[state->chunk_index++].chunk = (unsigned char*)&ccam_dma_buf[state->port_num][state->cirbuf_rp[port] >> 2];
state->data_segments = 1;
......@@ -719,6 +797,7 @@ int sendImageFrame(camogm_state *state)
case CAMOGM_FORMAT_MOV: rslt = camogm_frame_mov(state); break;
default: rslt = 0; // do nothing
}
if (rslt) {
D4(fprintf(debug_file, "sendImageFrame:12: camogm_frame_***() returned %d\n", rslt));
return rslt;
......@@ -726,17 +805,24 @@ int sendImageFrame(camogm_state *state)
if (state->kml_used) rslt = camogm_frame_kml(state); // will turn on state->kml_used if it can
if (rslt) return rslt;
D4(fprintf(debug_file, "_14_"));
// advance frame pointer
D4(fprintf(debug_file, "_14_ @ %07d ",get_fpga_usec(state->fd_fparmsall[0], 0)));
// advance frame pointer
state->frameno++;
state->cirbuf_rp[port] = lseek(state->fd_circ[port], LSEEK_CIRC_NEXT, SEEK_END);
if (state->cirbuf_rp[port] < 0) {
D0(fprintf(debug_file, "_14a_: sendImageFrame(): frame broken - buffer overrun\n"));
lseek(state->fd_circ[port], state->cirbuf_rp[port], SEEK_SET); // just in case - restore pointer
fp = lseek(state->fd_circ[port], LSEEK_CIRC_NEXT, SEEK_END); // Can it wait here?
// state->cirbuf_rp[port] = lseek(state->fd_circ[port], LSEEK_CIRC_NEXT, SEEK_END); // Can it wait here?
if (fp < 0) {
D0(fprintf(debug_file, "_14a_: sendImageFrame(): frame broken - buffer overrun, was state->cirbuf_rp[port]=0x%08x\n", state->cirbuf_rp[port]));
dbg_meta=state->cirbuf_rp[port] - 32;
if (dbg_meta < 0) dbg_meta += state->circ_buff_size[port];
D0(fprintf(debug_file,"Details on meta and first data port=%d, state->cirbuf_rp[port]=0x%08x\n",port,state->cirbuf_rp[port]); for (i=0; i< 16; i++) { \
fprintf(debug_file," 0x%08lx", ((unsigned long* )&ccam_dma_buf[port][dbg_meta >> 2])[i]); \
if ((i & 7) == 7) fprintf(debug_file,"\n"); });
return -CAMOGM_FRAME_BROKEN;
}
D3(fprintf(debug_file, "\tcompressed frame number: %li\n", lseek(state->fd_circ[port], LSEEK_CIRC_GETFRAME, SEEK_END)));
// optionally save it to global read pointer (i.e. for debugging with imgsrv "/pointers")
state->cirbuf_rp[port]=fp;
D4(fprintf(debug_file, "_14b_ compressed frame number: %li[%d] @ %07d\n", lseek(state->fd_circ[port], LSEEK_CIRC_GETFRAME, SEEK_END), port, get_fpga_usec(state->fd_fparmsall[0], 0)));
// optionally save it to global read pointer (i.e. for debugging with imgsrv "/pointers")
if (state->save_gp) lseek(state->fd_circ[port], LSEEK_CIRC_SETP, SEEK_END);
D4(fprintf(debug_file, "_15_\n"));
if (state->frames_skip > 0) {
......@@ -744,9 +830,8 @@ int sendImageFrame(camogm_state *state)
} else if (state->frames_skip < 0) {
state->frames_skip_left[port] += -(state->frames_skip);
}
D3(fprintf(debug_file,"cirbuf_rp to next frame = 0x%x\n", state->cirbuf_rp[port]));
D6(fprintf(debug_file, "Exit sendImageFrame() OK @ %07d\n",get_fpga_usec(state->fd_fparmsall[0], 0)));
D4(fprintf(debug_file,"_14c_ cirbuf_rp to next frame = 0x%x[%d], exit sendImageFrame() OK @ %07d\n", \
state->cirbuf_rp[port], port, get_fpga_usec(state->fd_fparmsall[0], 0)));
return 0;
}
......@@ -799,6 +884,7 @@ int camogm_stop(camogm_state *state)
pthread_mutex_lock(&state->mutex);
state->prog_state = STATE_STOPPED;
pthread_mutex_unlock(&state->mutex);
DFLUSH;
return 0;
}
......@@ -946,10 +1032,16 @@ void get_disk_info(camogm_state *state)
}
if (get_disk_range(state->rawdev.rawdev_path, &rng) == 0) {
state->writer_params.lba_start = rng.from;
state->writer_params.lba_end = rng.to;
state->writer_params.lba_current = state->writer_params.lba_start;
set_disk_range(&rng);
// align to 4K pages PAGE_PHYS=4096, PHY_BLOCK_SIZE=512
state->writer_params.lba_start = (rng.from / (PAGE_PHYS/PHY_BLOCK_SIZE)) * (PAGE_PHYS/PHY_BLOCK_SIZE);
if (state->writer_params.lba_start < rng.from){ // round up
state->writer_params.lba_start += PAGE_PHYS/PHY_BLOCK_SIZE;
}
state->writer_params.lba_end = (rng.to / (PAGE_PHYS/PHY_BLOCK_SIZE)) * (PAGE_PHYS/PHY_BLOCK_SIZE); // calculated as to+size
state->writer_params.next_segment_pos = 0; // in bytes
set_disk_range(&rng); // set to sysfs:/sys/devices/soc0/amba@0/80000000.elphel-ahci/lba_{start,end}
D2(fprintf(debug_file, "get_disk_info(): Disk parameters: lba_start=%lld, lba_end=%lld (first unusable)\n", \
state->writer_params.lba_start,state->writer_params.lba_end));
} else {
D0(fprintf(debug_file, "ERROR: unable to get disk size and starting sector\n"));
}
......@@ -1175,8 +1267,8 @@ void camogm_status(camogm_state *state, char * fn, int xml)
_kml_height_mode, state->kml_height, state->kml_period, state->kml_last_ts, state->kml_last_uts, \
state->greedy ? "yes" : "no", state->ignore_fps ? "yes" : "no", state->rawdev.rawdev_path,
state->rawdev.overrun, state->rawdev.curr_pos_w, state->rawdev.curr_pos_r, _percent_done,
state->writer_params.lba_start, state->writer_params.lba_current, state->writer_params.lba_end);
state->writer_params.lba_start, get_lba_next(&state->writer_params), state->writer_params.lba_end);
//get_lba_next
FOR_EACH_PORT(int, chn) {
char *_active = is_chn_active(state, chn) ? "yes" : "no";
fprintf(f,
......@@ -1253,7 +1345,7 @@ void camogm_status(camogm_state *state, char * fn, int xml)
fprintf(f, "kml_last_ts \t%d.%06d\n", state->kml_last_ts, state->kml_last_uts);
fprintf(f, "\n");
fprintf(f, "lba_start \t%llu\n", state->writer_params.lba_start);
fprintf(f, "lba_current \t%llu\n", state->writer_params.lba_current);
fprintf(f, "lba_current \t%llu\n", get_lba_next(&state->writer_params)); // state->writer_params.lba_current);
fprintf(f, "lba_end \t%llu\n", state->writer_params.lba_end);
fprintf(f, "\n");
FOR_EACH_PORT(int, chn) {
......@@ -1385,38 +1477,40 @@ int parse_cmd(camogm_state *state, FILE* npipe)
{
char * cmd;
char * args;
char * argse;
int d;
double dd;
int port = state->port_num;
//skip empty commands
while (((cmd = getLineFromPipe(npipe))) && !cmd[0]) ;
if (!cmd) return 0; // nothing in the pipe
D2(fprintf(debug_file, "Got command (state->port_num=%d): '%s' @ %07d -> ", port, cmd, get_fpga_usec(state->fd_fparmsall[0], 0)));
D3(syslog (LOG_INFO, "Got command (state->port_num=%d): '%s' @ %07d -> ", port, cmd, get_fpga_usec(state->fd_fparmsall[0], 0)));
if (port >= 0) {
D3(fprintf(debug_file, "Acknowledge received command for port=%d\n", port));
// Acknowledge received command by copying frame number to per-daemon parameter
// GLOBALPARS(state->port_num, G_DAEMON_ERR + lastDaemonBit[state->port_num]) = GLOBALPARS(state->port_num, G_THIS_FRAME);
setGValue(port, G_DAEMON_ERR + lastDaemonBit[port], getGPValue(port, G_THIS_FRAME));
}
args = strpbrk(cmd, "= \t");
// is it just a single word command or does it have parameters?
if (args) {
char * argse;
int d;
double dd;
int port = state->port_num;
uint64_t current_lba = state->writer_params.lba_start;
int res=0;
//skip empty commands
while (((cmd = getLineFromPipe(npipe))) && !cmd[0]) ;
if (!cmd) return 0; // nothing in the pipe
D2(fprintf(debug_file, "%s:line %d: Got command (state->port_num=%d): '%s' @ %07d -> ", __FILE__, __LINE__, port, cmd, get_fpga_usec(state->fd_fparmsall[0], 0)));
D4(syslog (LOG_INFO, "%s:line %d: Got command (state->port_num=%d): '%s' @ %07d -> ", __FILE__, __LINE__, port, cmd, get_fpga_usec(state->fd_fparmsall[0], 0)));
if (port >= 0) {
D3(fprintf(debug_file, "Acknowledge received command for port=%d\n", port));
// Acknowledge received command by copying frame number to per-daemon parameter
// GLOBALPARS(state->port_num, G_DAEMON_ERR + lastDaemonBit[state->port_num]) = GLOBALPARS(state->port_num, G_THIS_FRAME);
setGValue(port, G_DAEMON_ERR + lastDaemonBit[port], getGPValue(port, G_THIS_FRAME));
}
args = strpbrk(cmd, "= \t");
// is it just a single word command or does it have parameters?
if (args) {
args[0] = 0;
args++;
while (strchr("= \t", args[0])) args++;
if (args[0]) {
// ltrim (args)
// ltrim (args)
for (argse = strchr(args, '\0') - 1; strchr("= \t", argse[0]); argse--) argse[0] = '\0';
}
if (!args[0]) args = NULL;
}
// now cmd is trimmed, arg is NULL or a pointer to trimmed command arguments
// now cmd is trimmed, arg is NULL or a pointer to trimmed command arguments
if (strcmp(cmd, "start") == 0) {
check_compressors(state);
get_disk_info(state);
D6(fprintf(debug_file, "camogm_start() from %s:%d: @ %07d\n", __FILE__, __LINE__,get_fpga_usec(state->fd_fparmsall[0], 0)));
camogm_start(state);
return 1;
} else if (strcmp(cmd, "reset") == 0) { // will reset pointer to the last acquired frame (if any)
......@@ -1460,8 +1554,8 @@ int parse_cmd(camogm_state *state, FILE* npipe)
dd = strtod(args, NULL);
camogm_set_timescale(state, dd ? dd : 1.0);
return 10;
//TODO: fix period calculation/check for frame skipping (just disable in frame skip mode?)
//TODO: add time period (system clock), not just frame skipping
//TODO: fix period calculation/check for frame skipping (just disable in frame skip mode?)
//TODO: add time period (system clock), not just frame skipping
} else if (strcmp(cmd, "frameskip") == 0) {
......@@ -1546,7 +1640,19 @@ int parse_cmd(camogm_state *state, FILE* npipe)
camogm_set_prefix(state, args, RAW_PATH);
// make disk geometry available in status xml before recording starts
get_disk_info(state);
open_state_file(&state->rawdev, &state->writer_params.lba_current);
/// open_state_file(&state->rawdev, &state->writer_params.lba_current);
res = open_state_file(&state->rawdev, &current_lba);
if ((res != 0) || (current_lba < state->writer_params.lba_start) || (current_lba < state->writer_params.lba_start)) {
D0(fprintf(debug_file, "parse_cmd(): INVALID start lba or disk info: start = %llu, current = %llu, end = %llu, resetting current_lba to lba_start\n",
state->writer_params.lba_start, current_lba, state->writer_params.lba_end)); // incorrect
current_lba=state->writer_params.lba_start;
}
state->writer_params.next_segment_pos = lba_to_offset(current_lba - state->writer_params.lba_start);
D2(fprintf(debug_file, "parse_cmd(): Block device positions: start = %llu, current = %llu, end = %llu, params->next_segment_pos=0x%llx\n",
state->writer_params.lba_start, get_lba_next(&state->writer_params), state->writer_params.lba_end, state->writer_params.next_segment_pos)); // incorrect
D3(syslog (LOG_INFO, "parse_cmd(): lba_start = %lld, lba_end=%lld, current_lba=%lld, next_segment_pos=%lld",\
state->writer_params.lba_start, state->writer_params.lba_end, get_lba_next(&state->writer_params), state->writer_params.next_segment_pos));
} else {
state->rawdev_op = 0;
state->rawdev.rawdev_path[0] = '\0';
......@@ -1587,7 +1693,7 @@ void clean_up(camogm_state *state)
if (is_fd_valid(state->fd_fparmsall[port]))
close(state->fd_fparmsall[port]);
}
D2(fprintf(debug_file, "%s:line %d\n", __FILE__, __LINE__));
if (state->rawdev.thread_state != STATE_STOPPED) {
pthread_cancel(state->rawdev.tid);
}
......@@ -1605,7 +1711,7 @@ void clean_up(camogm_state *state)
int listener_loop(camogm_state *state)
{
FILE *cmd_file;
int rslt, ret, cmd, f_ok;
int rslt, ret, cmd, f_ok, i;
int fp0, fp1;
int process = 1;
int curr_port = 0;
......@@ -1692,6 +1798,7 @@ int listener_loop(camogm_state *state)
D6(fprintf(debug_file, "state->prog_state == STATE_RUNNING "));
if (curr_port < 0) {
rslt = CAMOGM_FRAME_BROKEN;
D1(fprintf(debug_file,"%s:line %d - rslt= %d (cur_port <0)\n", __FILE__, __LINE__, rslt));
} else {
rslt = -sendImageFrame(state);
}
......@@ -1700,6 +1807,21 @@ int listener_loop(camogm_state *state)
case 0:
break; // frame sent OK, nothing to do (TODO: check file length/duration)
case CAMOGM_FRAME_NOT_READY: // just wait for the frame to appear at the current pointer
pthread_mutex_lock(&state->writer_params.writer_mutex); // first time before the debug file is set !
state->writer_params.circbuf_many = false; // will be set when main (preparing) thread is ahead of the writer threads by more than NUM_NEED_NEMPTY
if ((rslt=get_write_page(&state->writer_params) > 0)) { // still some prepared pages left
pthread_cond_signal(&state->writer_params.writer_cond); // wake up other writer threads?
D3(fprintf(debug_file, "listener_loop(): some prepared pages remain (%d)[%d%d:%d%d] MANY=%d EMPTY=%d BUSY=%d [%d%d%d%d%d%d%d%d], SIGNALLED @ %07d\n",\
rslt, \
state->writer_params.writev_run[0],state->writer_params.writev_run[1], \
state->writer_params.write_waits_sig[0],state->writer_params.write_waits_sig[1], \
state->writer_params.circbuf_many, get_num_empty(&state->writer_params), get_num_busy(&state->writer_params), \
state->writer_params.chunk_page_state[0],state->writer_params.chunk_page_state[1],state->writer_params.chunk_page_state[2],state->writer_params.chunk_page_state[3], \
state->writer_params.chunk_page_state[4],state->writer_params.chunk_page_state[5],state->writer_params.chunk_page_state[6],state->writer_params.chunk_page_state[7], \
get_fpga_usec(state->fd_fparmsall[0], 0)));
}
pthread_mutex_unlock(&state->writer_params.writer_mutex);
// we'll wait for a frame, not to waste resources. But if the compressor is stopped this program will not respond to any commands
// TODO - add another wait with (short) timeout?
wait_frame_sync(state->fd_fparmsall[curr_port]);
......@@ -1715,6 +1837,8 @@ int listener_loop(camogm_state *state)
D0(fprintf(debug_file,"%s:line %d - sendImageFrame() returned -%d\n", __FILE__, __LINE__, rslt));
camogm_stop(state);
state->prog_state = STATE_RESTARTING;
D6(fprintf(debug_file, "camogm_start() from %s:%d: @ %07d\n", \
__FILE__, __LINE__,get_fpga_usec(state->fd_fparmsall[0], 0)));
camogm_start(state);
break;
case CAMOGM_FRAME_FILE_ERR: // error with file I/O
......@@ -1728,15 +1852,34 @@ int listener_loop(camogm_state *state)
} // switch sendImageFrame()
// collect error statistics
if (rslt > 0 && rslt < CAMOGM_ERRNUM)
state->error_stat[curr_port][rslt]++;
if (rslt > 0 && rslt < CAMOGM_ERRNUM) {
if ((curr_port >= 0) && (curr_port < SENSOR_PORTS)) {
state->error_stat[curr_port][rslt]++;
} else {
D0(fprintf(debug_file, "%s:line %d - *** Tried to write statistics for negative port %d, ERR= %d !!! ***\n", __FILE__, __LINE__, curr_port, rslt));
}
}
if ((rslt != 0) && (rslt != CAMOGM_FRAME_NOT_READY) && (rslt != CAMOGM_FRAME_CHANGED))
if ((rslt != 0) && (rslt != CAMOGM_FRAME_NOT_READY) && (rslt != CAMOGM_FRAME_CHANGED)) {
// add port number to error code to facilitate debugging
state->last_error_code = rslt + 100 * state->port_num;
}
if ((rslt != 0) && (rslt != CAMOGM_FRAME_NOT_READY)) {
// add port number to error code to facilitate debugging
state->last_error_code = rslt + 100 * state->port_num;
D0(fprintf(debug_file, "%s:%d: Error (code = %d), params->next_segment_pos=%llu @ %07d\n", \
__FILE__, __LINE__, rslt, state->writer_params.next_segment_pos, get_fpga_usec(state->fd_fparmsall[0], 0)));
D2(syslog (LOG_INFO, "%s:%d: Error (code = %d), params->next_segment_pos=%llu @ %07d", \
__FILE__, __LINE__, rslt, state->writer_params.next_segment_pos, get_fpga_usec(state->fd_fparmsall[0], 0)));
D2(fprintf(debug_file, "chunk_page_prep=%d\n", state->writer_params.chunk_page_prep);\
for (i=0; i < SEGMENTS_PAGES; i++){fprintf(debug_file, "port:%d remainder:%d\n", \
state->writer_params.dbg_data[i][0],state->writer_params.dbg_data[i][1]);});
//next_segment_pos
}
} else if (state->prog_state == STATE_STARTING) { // no commands in queue,starting (but not started yet)
// retry starting
D2(fprintf(debug_file, "camogm_start() from %s:%d: @ %07d\n", __FILE__, __LINE__,get_fpga_usec(state->fd_fparmsall[0], 0)));
switch ((rslt = -camogm_start(state))) {
case 0:
break; // file started OK, nothing to do
......@@ -1764,8 +1907,9 @@ int listener_loop(camogm_state *state)
} // switch camogm_start()
// collect error statistics
if (rslt > 0 && rslt < CAMOGM_ERRNUM)
if ((rslt > 0) && (rslt < CAMOGM_ERRNUM) && (curr_port >= 0) && (curr_port < SENSOR_PORTS)) {
state->error_stat[curr_port][rslt]++;
}
if ((rslt != 0) && (rslt != CAMOGM_TOO_EARLY) && (rslt != CAMOGM_FRAME_NOT_READY) && (rslt != CAMOGM_FRAME_CHANGED) )
// add port number to error code to facilitate debugging
......@@ -1784,9 +1928,12 @@ int listener_loop(camogm_state *state)
// } // if pfd.revents & POLLIN
#endif
} // while (process)
D2(fprintf(debug_file, "End of while (process) from %s:%d: @ %07d\n", \
__FILE__, __LINE__,get_fpga_usec(state->fd_fparmsall[0], 0)));
// normally, we should not be here
clean_up(state);
D2(fprintf(debug_file, "before EXIT_SUCCESS %s:%d: @ %07d\n", \
__FILE__, __LINE__,get_fpga_usec(state->fd_fparmsall[0], 0)));
return EXIT_SUCCESS;
}
......@@ -1974,10 +2121,12 @@ static int set_disk_range(const struct range *rng)
unsigned int select_port(camogm_state *state)
{
unsigned int chn = 0;
off_t free_sz;
off_t free_sz, valid_fp;
off_t file_pos;
off_t min_sz = -1;
int dbg_meta;
int port, i;
struct writer_params *params = &state->writer_params;
// define first active channel in case not all of them are active
for (int i = 0; i < SENSOR_PORTS; i++) {
if (is_chn_active(state, i)) {
......@@ -1987,42 +2136,99 @@ unsigned int select_port(camogm_state *state)
}
if (state->prog_state == STATE_STARTING || state->prog_state == STATE_RUNNING)
D6(fprintf(debug_file, "Selecting sensor port, buffer free size: "));
for (int i = 0; i < SENSOR_PORTS; i++) {
if (is_chn_active(state, i)) {
file_pos = lseek(state->fd_circ[i], 0, SEEK_CUR);
if (file_pos != -1) {
free_sz = lseek(state->fd_circ[i], LSEEK_CIRC_FREE, SEEK_END); // maybe ==-1 if new frame is not yet ready?
lseek(state->fd_circ[i], file_pos, SEEK_SET);
if (state->prog_state == STATE_STARTING || state->prog_state == STATE_RUNNING)
D6(fprintf(debug_file, "port %i = %li, ", i, free_sz));
for (port = 0; port < SENSOR_PORTS; port++) {
if (is_chn_active(state, port)) {
file_pos = lseek(state->fd_circ[port], state->cirbuf_rp[port], SEEK_SET); // new - file pointer seems to be lost somewhere
/// file_pos = lseek(state->fd_circ[i], 0, SEEK_CUR);
if (file_pos < 0){
D1(fprintf(debug_file, "%s:line %d file pointer for %d seems broken\n", __FILE__, __LINE__, port));
// file_pos = lseek(state->fd_circ[i], state->cirbuf_rp[i], SEEK_SET);
}
#if 0
if (file_pos < 0){
// wait_frame_sync(const int fd_fparsall)
D1(fprintf(debug_file, "port %i seems broken, trying to wait for one frame", port));
wait_frame_sync(state->fd_fparmsall[port]);
file_pos = lseek(state->fd_circ[port], 0, SEEK_CUR);
}
#endif
if (file_pos >= 0) {
free_sz = lseek(state->fd_circ[port], LSEEK_CIRC_FREE, SEEK_END); // maybe ==-1 if new frame is not yet ready?
lseek(state->fd_circ[port], file_pos, SEEK_SET);
valid_fp = lseek(state->fd_circ[port], LSEEK_CIRC_VALID, SEEK_END);
lseek(state->fd_circ[port], file_pos, SEEK_SET);
if (state->prog_state == STATE_STARTING || state->prog_state == STATE_RUNNING) {
if (free_sz < 0){
D1(fprintf(debug_file, "%s:line %d chn %d: file_pos = 0x%08lx, wp = 0x%08lx free = %ld, valid = %ld\n",\
__FILE__, __LINE__, port, file_pos, lseek(state->fd_circ[port], LSEEK_CIRC_TOWP, SEEK_END), free_sz, valid_fp));
file_pos = lseek(state->fd_circ[port], state->cirbuf_rp[port], SEEK_SET);
dbg_meta=state->cirbuf_rp[port] - 32;
if (dbg_meta < 0) dbg_meta += state->circ_buff_size[port];
D0(fprintf(debug_file,"Details on meta and first data port=%d, state->cirbuf_rp[port]=0x%08x\n",\
port,state->cirbuf_rp[port]); for (i=0; i< 16; i++) { \
fprintf(debug_file," 0x%08lx", ((unsigned long* )&ccam_dma_buf[port][dbg_meta >> 2])[i]); \
if ((i & 7) == 7) fprintf(debug_file,"\n"); });
chn = -1; // port;
break;
}
D6(fprintf(debug_file, "port %i = %li, ", port, free_sz));
}
if ((free_sz < min_sz && free_sz >= 0) || min_sz == -1) {
if (free_sz < CAMOGM_MIN_BUF_FRAMES * state->image_size[i]) {
D3(fprintf(debug_file, "port %i has too little free space (%ld <CAMOGM_MIN_BUF_FRAMES * %d), considered BROKEN, state=%d", \
i, free_sz, state->image_size[i], state->prog_state));
chn = -1; // i;
if (free_sz < CAMOGM_MIN_BUF_FRAMES * state->image_size[port]) {
if ((state->prog_state == STATE_STARTING) || (state->prog_state == STATE_RUNNING)) {
D1(fprintf(debug_file, "port %i has too little free space (%ld <CAMOGM_MIN_BUF_FRAMES * %d), considered BROKEN, state=%d\n", \
port, free_sz, state->image_size[port], state->prog_state));
}
chn = -1; // port;
break;
}
min_sz = free_sz;
chn = i;
chn = port;
}
} else {
if (state->prog_state == STATE_STARTING || state->prog_state == STATE_RUNNING)
D3(fprintf(debug_file, "port %i BROKEN FRAME, ", i));
D1(fprintf(debug_file, "ERROR: port %i BROKEN FRAME, state=%d", port, state->prog_state));
// current frame pointer is possibly overwritten (buffer overflow), select
// this channel which will force sendImageFrame() to take recovery actions
// Did not work - mMaybe use -1? Make susre at least one extra frame is OK?
chn = -1; // i;
// Did not work - Maybe use -1? Make sure at least one extra frame is OK?
chn = -1; // port;
break;
}
} else {
if (state->prog_state == STATE_STARTING || state->prog_state == STATE_RUNNING)
D6(fprintf(debug_file, "port %i is inactive, ", i));
D6(fprintf(debug_file, "port %i is inactive, ", port));
}
}
if ((state->prog_state == STATE_STARTING) || (state->prog_state == STATE_RUNNING)) {
D1(if ((chn >=0) && (lseek(state->fd_circ[chn], LSEEK_CIRC_READY, SEEK_END) >= 0)) \
fprintf(debug_file, "selected port: %i, min free size = %08ld ", chn, min_sz); \
#ifdef MIN_USED_SIZE
D1(if ((chn >=0) && (lseek(state->fd_circ[chn], LSEEK_CIRC_READY, SEEK_END) >= 0) && \
(lseek(state->fd_circ[chn], LSEEK_CIRC_USED, SEEK_END) >= MIN_USED_SIZE)) {\
fprintf(debug_file, "sel port: %i, min free = %08ld, MANY=%d EMPTY=%d BUSY=%d [%d%d%d%d%d%d%d%d] @ %07d ", \
chn, min_sz, \
state->writer_params.circbuf_many, get_num_empty(&state->writer_params), get_num_busy(&state->writer_params), \
params->chunk_page_state[0],params->chunk_page_state[1],params->chunk_page_state[2],params->chunk_page_state[3], \
params->chunk_page_state[4],params->chunk_page_state[5],params->chunk_page_state[6],params->chunk_page_state[7], \
get_fpga_usec(state->fd_fparmsall[0], 0)); if (debug_level < 2) fprintf(debug_file,"\n");}\
else if (debug_level >= 6) fprintf(debug_file,"no port is ready @ %07d\n", get_fpga_usec(state->fd_fparmsall[0], 0)));
#else
D1(if ((chn >=0) && (lseek(state->fd_circ[chn], LSEEK_CIRC_READY, SEEK_END) >= 0)) {\
fprintf(debug_file, "sel port: %i, min free = %08ld, MANY=%d EMPTY=%d BUSY=%d [%d%d%d%d%d%d%d%d] @ %07d ", \
chn, min_sz, \
state->writer_params.circbuf_many, get_num_empty(&state->writer_params), get_num_busy(&state->writer_params), \
params->chunk_page_state[0],params->chunk_page_state[1],params->chunk_page_state[2],params->chunk_page_state[3], \
params->chunk_page_state[4],params->chunk_page_state[5],params->chunk_page_state[6],params->chunk_page_state[7], \
get_fpga_usec(state->fd_fparmsall[0], 0)); if (debug_level < 2) fprintf(debug_file,"\n");}\
else if (debug_level >= 6) fprintf(debug_file,"no port is ready @ %07d\n", get_fpga_usec(state->fd_fparmsall[0], 0)));
#endif
if (chn >= 0){
lseek(state->fd_circ[chn], state->cirbuf_rp[chn], SEEK_SET); //restore after debug?
}
}
return chn;
......@@ -2202,8 +2408,15 @@ int main(int argc, char *argv[])
}
sstate.rawdev.thread_state = STATE_RUNNING;
str_len = strlen(state_name_str);
// log file not yet switched from default
D2(fprintf(debug_file, "%s:line %d : Started reader thread ID=%ld @ %07d\n", \
__FILE__, __LINE__, sstate.rawdev.tid, get_fpga_usec(sstate.fd_fparmsall[0], 0)));
D2(syslog (LOG_INFO, "%s:line %d : Started reader thread ID=%ld @ %07d\n", \
__FILE__, __LINE__, sstate.rawdev.tid, get_fpga_usec(sstate.fd_fparmsall[0], 0)));
if (str_len > 0) {
strncpy(sstate.rawdev.state_path, (const char *)state_name_str, str_len + 1);
}
ret = listener_loop(&sstate);
......@@ -2306,6 +2519,14 @@ inline int get_fpga_usec(const int fd_fparsall, unsigned int port)
return (int) GLOBALPARS(port, G_MICROSECONDS) + 1000000*(GLOBALPARS(port, G_SECONDS) % 10);
}
inline uint64_t get_fpga_time64(const int fd_fparsall, unsigned int port)
{
lseek(fd_fparsall, LSEEK_GET_FPGA_TIME, SEEK_END);
// return (int) getGPValue(port, G_MICROSECONDS);
return GLOBALPARS(port, G_MICROSECONDS) + 1000000 *(uint64_t) GLOBALPARS(port, G_SECONDS);
}
/**
* @brief Wait for frame sync
*/
......
......@@ -18,7 +18,7 @@
#ifndef _CAMOGM_H
#define _CAMOGM_H
#define __USE_GNU // for O_DIRECT
//#define __USE_GNU // for O_DIRECT
//#define USE_POLL
#include <pthread.h>
#include <stdbool.h>
......@@ -30,6 +30,11 @@
#include <syslog.h>
#include <malloc.h> // debugging
#define NUM_WRITER_THREADS 2
#define NUM_NEED_EMPTY (NUM_WRITER_THREADS + 1) // run main thread to prepage more pages if >= empty
#define NUM_NEED_NEMPTY (NUM_WRITER_THREADS + 0) // wake up main thread if there are at least this number of empty pages
#define NUM_FREE_MANY (NUM_WRITER_THREADS + 1) // Set many if get_num_empty() < NUM_FREE_MANY
//#define NODEBUG
#define CAMOGM_FRAME_NOT_READY 1 ///< frame pointer valid, but not yet acquired
#define CAMOGM_FRAME_INVALID 2 ///< invalid frame pointer
......@@ -49,6 +54,8 @@
#define CAMOGM_FORMAT_MOV 3 ///< output as Apple Quicktime
#define CAMOGM_MIN_BUF_FRAMES 2 ///< buffer should accomodate at list this number of frames
#define FLUSH_DEBUG 1 ///< flush debug file after each print
#ifdef NODEBUG
#define D(x)
#define D0(x)
......@@ -60,7 +67,9 @@
#define D6(x)
#define D7(x)
#define DD(x)
#define DFLUSH
#else
# if FLUSH_DEBUG
#define D(x) { if (debug_file && debug_level) { x; fflush(debug_file); } }
#define D0(x) { if (debug_file) { pthread_mutex_lock(&print_mutex); x; fflush(debug_file); pthread_mutex_unlock(&print_mutex); } }
#define D1(x) { if (debug_file && (debug_level > 0)) { pthread_mutex_lock(&print_mutex); x; fflush(debug_file); pthread_mutex_unlock(&print_mutex); } }
......@@ -72,6 +81,21 @@
//#define D7(x) { if (debug_file && (debug_level > 6)) { pthread_mutex_lock(&print_mutex); x; fflush(debug_file); pthread_mutex_unlock(&print_mutex); } }
#define D7(x)
#define DD(x) { if (debug_file) { fprintf(debug_file, "%s:%d:", __FILE__, __LINE__); x; fflush(debug_file); } }
#else // if FLUSH_DEBUG
#define D(x) { if (debug_file && debug_level) { x; } }
#define D0(x) { if (debug_file) { pthread_mutex_lock(&print_mutex); x; pthread_mutex_unlock(&print_mutex); } }
#define D1(x) { if (debug_file && (debug_level > 0)) { pthread_mutex_lock(&print_mutex); x; pthread_mutex_unlock(&print_mutex); } }
#define D2(x) { if (debug_file && (debug_level > 1)) { pthread_mutex_lock(&print_mutex); x; pthread_mutex_unlock(&print_mutex); } }
#define D3(x) { if (debug_file && (debug_level > 2)) { pthread_mutex_lock(&print_mutex); x; pthread_mutex_unlock(&print_mutex); } }
#define D4(x) { if (debug_file && (debug_level > 3)) { pthread_mutex_lock(&print_mutex); x; pthread_mutex_unlock(&print_mutex); } }
#define D5(x) { if (debug_file && (debug_level > 4)) { pthread_mutex_lock(&print_mutex); x; pthread_mutex_unlock(&print_mutex); } }
#define D6(x) { if (debug_file && (debug_level > 5)) { pthread_mutex_lock(&print_mutex); x; pthread_mutex_unlock(&print_mutex); } }
//#define D7(x) { if (debug_file && (debug_level > 6)) { pthread_mutex_lock(&print_mutex); x; pthread_mutex_unlock(&print_mutex); } }
#define D7(x)
#define DD(x) { if (debug_file) { fprintf(debug_file, "%s:%d:", __FILE__, __LINE__); x; } }
#endif // if FLUSH_DEBUG else
#define DFLUSH { if (debug_file ) { pthread_mutex_lock(&print_mutex);fflush(debug_file); pthread_mutex_unlock(&print_mutex); } }
#endif
/** @brief HEADER_SIZE is defined to be larger than actual header (with EXIF) to use compile-time buffer */
......@@ -92,6 +116,8 @@
#define JPEG_TRAILER_LEN 2 ///< The size in bytes of JPEG trailer
#define CIRCBUF_ALIGNMENT_SIZE 32 ///< Align of CIRCBUF entries
#define MIN_USED_SIZE 4000000 // debugging DMA clashes
// Switching CHUNK -> SEGMENT
#define PAGE_PHYS 4096 // 512 // alignment size for O_DIRECT, may be different from LBA block size that is 512
enum segments {
......@@ -101,7 +127,13 @@ enum segments {
SEGMENTS_NUMBER ///< Just to get number of segment types
};
#define SEGMENTS_PAGES 4 // 2
enum segpage_states {
SEGPAGE_EMPTY = 0, ///< page of segments is empty (new or written to disk)
SEGPAGE_FULL, ///< page of segments is prepared for writing
SEGPAGE_BUSY ///< page of segments is in the process of being sent to fisk
};
#define SEGMENTS_PAGES 8 // 4 // 2
#define SEGMENTS_TOTAL ((SEGMENTS_NUMBER) * (SEGMENTS_PAGES))
/** Glue buffer should be large enough to contain:
......@@ -203,29 +235,35 @@ typedef struct {
* @brief Contains mutexes and conditional variables associated with disk writing thread
*/
struct writer_params {
int blockdev_fd; ///< file descriptor for open block device where frame will be recorded
pthread_t writer_thread; ///< disk writing thread
// int blockdev_fd; ///< file descriptor for open block device where frame will be recorded
pthread_t writer_threads [NUM_WRITER_THREADS]; ///< array of disk writer threads
bool writev_run [NUM_WRITER_THREADS]; ///< writev() is active (enable main thread if buffer not full and writew or buf empty)
bool write_waits_sig [NUM_WRITER_THREADS]; ///< This write thread is waiting for signal
bool write_go [NUM_WRITER_THREADS]; ///< This writer thread may go even if other thread already left write_waits_sig state
int writev_run_segm [NUM_WRITER_THREADS]; ///< current writev segment being written per thread (normally segment0 is small, other - big
pthread_mutex_t writer_mutex; ///< synchronization mutex for main and writing threads
pthread_cond_t writer_cond; ///< conditional variable indicating that writer thread can proceed with new frame
pthread_cond_t main_cond; ///< conditional variable indicating that main thread can update write pointers
bool circbuf_many; ///< probably there are many more frames in circbuf
int chunk_page_prep; ///< page of chunks being prepared. Incremented (mod) after data is prepared for raw write
int chunk_page_write; ///< page of chunks to write. Incremented (mod) after recording to disk
bool writev_run; ///< writev() is active (enable main thread if buffer not full and writew or buf empty)
int chunk_page_state[SEGMENTS_PAGES]; ///< segments page state: 0 - empty, 1 - full, 2 - currently writing to disk
uint64_t next_segment_pos; ///< next segment calculated file position (bytes)
uint64_t segment_pos [SEGMENTS_PAGES]; ///< per-segment file offsets
int last_ret_val; ///< error value return during last frame recording (if any occurred)
bool exit_thread; ///< flag indicating that the writing thread should terminate
bool exit_write_threads; ///< flag indicating that the writing threads should terminate and close their files
int state; ///< the state of disk writing thread
struct iovec *data_segments[SEGMENTS_PAGES]; ///< a set of vectors sets pointing to aligned frame data buffers
unsigned char *common_buffs[FILE_CHUNKS_PAGES]; ///< buffer for aligned JPEG header // make multiple?
int dbg_data [SEGMENTS_PAGES][2]; ///< debug data: channel and length of copied tail
unsigned char *glue_buffs[SEGMENTS_PAGES + 1]; ///< glue buffer (end of previous frame + start of this one
// unsigned char *glue_buffs_carry; ///< extra glue buffer to pass from the previous to the next frame
struct iovec glue_carry_vec; ///< current tail pointer of the carry glue segment
uint64_t lba_start; ///< disk starting LBA
uint64_t lba_current; ///< current write position in LBAs
uint64_t lba_end; ///< disk last LBA
time_t stat_update; ///< time when status file was updated
uint64_t stat_update; ///< time when status file was updated
bool dummy_read; ///< enable dummy read cycle (debug feature)
};
/**
* @struct camogm_state
* @brief Holds current state of the running program
......@@ -242,13 +280,13 @@ typedef struct {
int fd_circ[SENSOR_PORTS]; ///< file descriptor for circbuf
int fd_head[SENSOR_PORTS]; ///< file descriptor for JPEG header
int fd_fparmsall[SENSOR_PORTS]; ///< file descriptor for sensor/compressor parameters
int fd_exif[SENSOR_PORTS]; ///< file descriptor for Exif data
int fd_tiff[SENSOR_PORTS]; ///< file descriptor for Tiff data
int head_size[SENSOR_PORTS]; ///< JPEG header size
int fd_exif[SENSOR_PORTS]; ///< file descriptor for Exif data
int fd_tiff[SENSOR_PORTS]; ///< file descriptor for Tiff data
int head_size[SENSOR_PORTS]; ///< JPEG header size
int image_size[SENSOR_PORTS]; ///< last image size in bytes
unsigned char jpegHeader[SENSOR_PORTS][JPEG_HEADER_MAXSIZE];
int metadata_start;
struct interframe_params_t frame_params[SENSOR_PORTS];
unsigned char jpegHeader[SENSOR_PORTS][JPEG_HEADER_MAXSIZE];
int metadata_start;
struct interframe_params_t frame_params[SENSOR_PORTS];
struct interframe_params_t this_frame_params[SENSOR_PORTS];
int jpeg_len;
int frame_period[SENSOR_PORTS]; ///< in microseconds (1/10 of what is needed for the Ogm header)
......@@ -337,8 +375,8 @@ int waitDaemonEnabled(unsigned int port, int daemonBit);
int isDaemonEnabled(unsigned int port, int daemonBit);
int is_fd_valid(int fd);
int get_fpga_usec(const int fd_fparsall, unsigned int port);
inline void wait_frame_sync(const int fd_fparsall);
uint64_t get_fpga_time64(const int fd_fparsall, unsigned int port);
void wait_frame_sync(const int fd_fparsall);
unsigned long *get_ccam_dma_buf(int port);
#endif /* _CAMOGM_H */
......@@ -95,7 +95,7 @@ static inline unsigned char *vectrpos(struct iovec *vec, size_t offset)
/** extend to page-aligned */
int vectaligntail(struct iovec *dest)
{
unsigned char *d = (unsigned char *)dest->iov_base;
// unsigned char *d = (unsigned char *)dest->iov_base;
int len = dest->iov_len;
int ceil_size = (len & (PAGE_PHYS - 1)) ? ((len | (PAGE_PHYS - 1)) + 1) : len;
if (ceil_size > len) {
......@@ -116,22 +116,23 @@ size_t remap_vectors(camogm_state *state) // , struct iovec *chunks)
// state->writer_params.glue_carry_vec has data for the the trailer of the previous frame. swap it with the
// state->writer_params.glue_buffs[seg_page][SEGMENT_GLUE]
size_t total_sz;
int seg_page = (state->writer_params.chunk_page_prep % FILE_CHUNKS_PAGES);
struct writer_params *params = &state->writer_params;
int seg_page = params->chunk_page_prep;
long head_bytes, tail_bytes, trimmed_bytes;
int i, data_index, data_pre, num_glue_pages, trim_segment, circbuff_offs;
int gap_size;
unsigned char * aligned_data;
struct iovec glue_vec;
struct iovec * glue_vec_p; // = &glue_vec;
glue_vec.iov_base = state->writer_params.glue_carry_vec.iov_base;
glue_vec.iov_len = state->writer_params.glue_carry_vec.iov_len;
glue_vec.iov_base = params->glue_carry_vec.iov_base;
glue_vec.iov_len = params->glue_carry_vec.iov_len;
D7(fprintf(debug_file, "_13c01_:remap_vectors @ %07d\n",get_fpga_usec(state->fd_fparmsall[0], 0)));
state->writer_params.glue_carry_vec.iov_base = state->writer_params.data_segments[seg_page][SEGMENT_GLUE].iov_base;
state->writer_params.glue_carry_vec.iov_len = 0; // state->writer_params.data_segments[seg_page][SEGMENT_GLUE].iov_len;
state->writer_params.data_segments[seg_page][SEGMENT_GLUE].iov_base = glue_vec.iov_base;
state->writer_params.data_segments[seg_page][SEGMENT_GLUE].iov_len = glue_vec.iov_len;
state->writer_params.data_segments[seg_page][SEGMENT_FIRST].iov_len = 0; // check iov_len first before using iov_base
state->writer_params.data_segments[seg_page][SEGMENT_SECOND].iov_len = 0; // check iov_len first before using iov_base
params->glue_carry_vec.iov_base = params->data_segments[seg_page][SEGMENT_GLUE].iov_base;
params->glue_carry_vec.iov_len = 0; // params->data_segments[seg_page][SEGMENT_GLUE].iov_len;
params->data_segments[seg_page][SEGMENT_GLUE].iov_base = glue_vec.iov_base;
params->data_segments[seg_page][SEGMENT_GLUE].iov_len = glue_vec.iov_len;
params->data_segments[seg_page][SEGMENT_FIRST].iov_len = 0; // check iov_len first before using iov_base
params->data_segments[seg_page][SEGMENT_SECOND].iov_len = 0; // check iov_len first before using iov_base
// calculate total header size
head_bytes = 0;
for (i = 0; i < state->chunk_data_index; i++){
......@@ -146,14 +147,14 @@ size_t remap_vectors(camogm_state *state) // , struct iovec *chunks)
circbuff_offs = (int) state->packetchunks[data_index].chunk;
aligned_data = state->packetchunks[data_index].chunk+ (PAGE_PHYS_CEIL(circbuff_offs) - circbuff_offs);
data_pre = aligned_data - state->packetchunks[data_index].chunk;
glue_vec_p = &state->writer_params.data_segments[seg_page][SEGMENT_GLUE];
glue_vec_p = &params->data_segments[seg_page][SEGMENT_GLUE];
D7(fprintf(debug_file, "_13c03_:remap_vectors, circbuff_offs=0x%x aligned_data=%p data_pre=0x%x @ %07d\n", circbuff_offs, aligned_data, data_pre, get_fpga_usec(state->fd_fparmsall[0], 0)));
D7(fprintf(debug_file, "_13c04_:remap_vectors, state->packetchunks[state->chunk_data_index].bytes=%ld @ %07d\n", state->packetchunks[state->chunk_data_index].bytes, get_fpga_usec(state->fd_fparmsall[0], 0)));
if (state->packetchunks[state->chunk_data_index].bytes < data_pre) {
// Too short frame data - special treatment with zero data, moving some to carry, and possibly outputing some from the GLUE
// there is definitely no second data - otherwise the first segment would end at page boundary
// copy everything to state->writer_params.data_segments[seg_page][SEGMENT_GLUE], trim to pages, pass residual to
// state->writer_params.glue_carry_vec
// copy everything to params->data_segments[seg_page][SEGMENT_GLUE], trim to pages, pass residual to
// params->glue_carry_vec
// copy header data, no gap
for (i = 0; i <= state->chunk_index; i++){ // including data and trailer
vectcpy(glue_vec_p, state->packetchunks[i].chunk, state->packetchunks[i].bytes);
......@@ -161,7 +162,7 @@ size_t remap_vectors(camogm_state *state) // , struct iovec *chunks)
trimmed_bytes = PAGE_PHYS_TRIM(glue_vec_p->iov_len);
D7(fprintf(debug_file, "_13c05_:remap_vectors, trimmed_bytes=%ld @ %07d\n", trimmed_bytes, get_fpga_usec(state->fd_fparmsall[0], 0)));
if ((glue_vec_p->iov_len - trimmed_bytes) > 0){
vectcpy(&state->writer_params.glue_carry_vec,
vectcpy(&params->glue_carry_vec,
((char *) glue_vec_p->iov_base) + trimmed_bytes,
glue_vec_p->iov_len - trimmed_bytes);
glue_vec_p->iov_len = trimmed_bytes; // may be 0
......@@ -178,7 +179,7 @@ size_t remap_vectors(camogm_state *state) // , struct iovec *chunks)
D7(fprintf(debug_file, "_13c06a0_:malloc_usable_size(glue_vec_p->iov_base) = %d @ %07d\n", malloc_usable_size(glue_vec_p->iov_base), get_fpga_usec(state->fd_fparmsall[0], 0)));
for (i = 0; i < SEGMENTS_PAGES; i++){
D7(fprintf(debug_file, "_13c06a1_: malloc_usable_size([%d][SEGMENT_GLUE]) = %d @ %07d\n",\
i, malloc_usable_size(state->writer_params.data_segments[i][SEGMENT_GLUE].iov_base), get_fpga_usec(state->fd_fparmsall[0], 0)));
i, malloc_usable_size(params->data_segments[i][SEGMENT_GLUE].iov_base), get_fpga_usec(state->fd_fparmsall[0], 0)));
}
// size_t malloc_usable_size(void *ptr);
......@@ -197,44 +198,49 @@ size_t remap_vectors(camogm_state *state) // , struct iovec *chunks)
vectcpy(glue_vec_p, state->packetchunks[state->chunk_data_index].chunk, data_pre);
// TODO: verify that glue_vec_p->iov_len is multiple of PAGE_PHYS
trim_segment = SEGMENT_FIRST;
state->writer_params.data_segments[seg_page][SEGMENT_FIRST].iov_base = ((char *) state->packetchunks[state->chunk_data_index].chunk) + data_pre;
state->writer_params.data_segments[seg_page][SEGMENT_FIRST].iov_len = state->packetchunks[state->chunk_data_index].bytes - data_pre;
params->data_segments[seg_page][SEGMENT_FIRST].iov_base = ((char *) state->packetchunks[state->chunk_data_index].chunk) + data_pre;
params->data_segments[seg_page][SEGMENT_FIRST].iov_len = state->packetchunks[state->chunk_data_index].bytes - data_pre;
D7(fprintf(debug_file, "_13c07_:remap_vectors @ %07d\n", get_fpga_usec(state->fd_fparmsall[0], 0)));
if (state->writer_params.data_segments[seg_page][SEGMENT_FIRST].iov_len == 0) {
if (params->data_segments[seg_page][SEGMENT_FIRST].iov_len == 0) {
// nothing left of data segment 1 - move segment2 to segment1 (or skip both)
if (state->data_segments > 1) { // move second segment to first
state->writer_params.data_segments[seg_page][SEGMENT_FIRST].iov_base = ((char *) state->packetchunks[state->chunk_data_index + 1].chunk);
state->writer_params.data_segments[seg_page][SEGMENT_FIRST].iov_len = state->packetchunks[state->chunk_data_index + 1].bytes ;
params->data_segments[seg_page][SEGMENT_FIRST].iov_base = ((char *) state->packetchunks[state->chunk_data_index + 1].chunk);
params->data_segments[seg_page][SEGMENT_FIRST].iov_len = state->packetchunks[state->chunk_data_index + 1].bytes ;
}
} else if (state->data_segments > 1) {
state->writer_params.data_segments[seg_page][SEGMENT_SECOND].iov_base = ((char *) state->packetchunks[state->chunk_data_index + 1].chunk);
state->writer_params.data_segments[seg_page][SEGMENT_SECOND].iov_len = state->packetchunks[state->chunk_data_index + 1].bytes ;
params->data_segments[seg_page][SEGMENT_SECOND].iov_base = ((char *) state->packetchunks[state->chunk_data_index + 1].chunk);
params->data_segments[seg_page][SEGMENT_SECOND].iov_len = state->packetchunks[state->chunk_data_index + 1].bytes ;
trim_segment = SEGMENT_SECOND;
}
D7(fprintf(debug_file, "_13c08_:remap_vectors,[SEGMENT_GLUE].iov_len=%d, [SEGMENT_FIRST].iov_len=%d, [SEGMENT_SECOND].iov_len=%d @ %07d\n", \
state->writer_params.data_segments[seg_page][SEGMENT_GLUE].iov_len, \
state->writer_params.data_segments[seg_page][SEGMENT_FIRST].iov_len, \
state->writer_params.data_segments[seg_page][SEGMENT_SECOND].iov_len, \
params->data_segments[seg_page][SEGMENT_GLUE].iov_len, \
params->data_segments[seg_page][SEGMENT_FIRST].iov_len, \
params->data_segments[seg_page][SEGMENT_SECOND].iov_len, \
get_fpga_usec(state->fd_fparmsall[0], 0)));
// Trim last data segment to PAGE_PHYS (it may make its length zero - OK), copy remaining
// data to the buffer pointed by state->writer_params.glue_carry_vec.iov_base, set state->writer_params.glue_carry_vec.iov_len
trimmed_bytes = PAGE_PHYS_TRIM(state->writer_params.data_segments[seg_page][trim_segment].iov_len);
// data to the buffer pointed by params->glue_carry_vec.iov_base, set params->glue_carry_vec.iov_len
trimmed_bytes = PAGE_PHYS_TRIM(params->data_segments[seg_page][trim_segment].iov_len);
D7(fprintf(debug_file, "_13c09_:remap_vectors, trimmed_bytes=%ld @ %07d\n", trimmed_bytes, get_fpga_usec(state->fd_fparmsall[0], 0)));
if (state->writer_params.data_segments[seg_page][trim_segment].iov_len > trimmed_bytes) {
// debugging dma-related bugs.
params->dbg_data[seg_page][0] = state->port_num;
params->dbg_data[seg_page][1] = params->data_segments[seg_page][trim_segment].iov_len - trimmed_bytes; // remainder copied to glue buffer
if (params->data_segments[seg_page][trim_segment].iov_len > trimmed_bytes) {
D6(fprintf(debug_file, "_13c09a:remap_vectors, trim_segment=%d, dest = %p, dest_len=%d src=%p, copy_len = %ld @ %07d\n", \
trim_segment,
&state->writer_params.glue_carry_vec, \
state->writer_params.glue_carry_vec.iov_len, \
((char *) state->writer_params.data_segments[seg_page][trim_segment].iov_base) + trimmed_bytes, \
state->writer_params.data_segments[seg_page][trim_segment].iov_len - trimmed_bytes, \
&params->glue_carry_vec, \
params->glue_carry_vec.iov_len, \
((char *) params->data_segments[seg_page][trim_segment].iov_base) + trimmed_bytes, \
params->data_segments[seg_page][trim_segment].iov_len - trimmed_bytes, \
get_fpga_usec(state->fd_fparmsall[0], 0)));
vectcpy(&state->writer_params.glue_carry_vec,
((char *) state->writer_params.data_segments[seg_page][trim_segment].iov_base) + trimmed_bytes,
state->writer_params.data_segments[seg_page][trim_segment].iov_len - trimmed_bytes);
state->writer_params.data_segments[seg_page][trim_segment].iov_len = trimmed_bytes; // reduce data segment length
vectcpy(&params->glue_carry_vec,
((char *) params->data_segments[seg_page][trim_segment].iov_base) + trimmed_bytes,
params->data_segments[seg_page][trim_segment].iov_len - trimmed_bytes);
params->data_segments[seg_page][trim_segment].iov_len = trimmed_bytes; // reduce data segment length
}
D7(fprintf(debug_file, "_13c09b_:remap_vectors, tail_segm=%d, chunk_index=%d @ %07d\n", \
state->chunk_data_index + state->data_segments, \
state->chunk_index, \
......@@ -243,13 +249,13 @@ size_t remap_vectors(camogm_state *state) // , struct iovec *chunks)
for (i = state->chunk_data_index + state->data_segments; i < state->chunk_index; i++){ // trailer if it exists
D7(fprintf(debug_file, "_13c09c_:remap_vectors, i=%d, dest = %p, dest_len=%d src=%p, len =%ld @ %07d\n", \
i, \
&state->writer_params.glue_carry_vec, \
state->writer_params.glue_carry_vec.iov_len, \
&params->glue_carry_vec, \
params->glue_carry_vec.iov_len, \
state->packetchunks[i].chunk, \
state->packetchunks[i].bytes, \
get_fpga_usec(state->fd_fparmsall[0], 0)));
vectcpy(&state->writer_params.glue_carry_vec,
vectcpy(&params->glue_carry_vec,
state->packetchunks[i].chunk,
state->packetchunks[i].bytes);
}
......@@ -257,7 +263,7 @@ size_t remap_vectors(camogm_state *state) // , struct iovec *chunks)
D7(fprintf(debug_file, "_13c10_:remap_vectors @ %07d\n", get_fpga_usec(state->fd_fparmsall[0], 0)));
total_sz = 0; // get_size_from_paged(state, 0, INCLUDE_REM) + rbuff->iov_len;
for (i = 0; i < SEGMENTS_NUMBER; i++){
total_sz += state->writer_params.data_segments[seg_page][i].iov_len;
total_sz += params->data_segments[seg_page][i].iov_len;
}
//port_num
state->image_size[state->port_num] = total_sz;
......@@ -268,30 +274,31 @@ size_t remap_vectors(camogm_state *state) // , struct iovec *chunks)
/** Allocate and initialize buffers for frame alignment */
int init_align_buffers(camogm_state *state)
{
D3(fprintf(debug_file, "GLUE_SEGMENTS = %d pages of %d bytes each\n", GLUE_SEGMENTS, PAGE_PHYS));
struct writer_params *params = &state->writer_params;
int seg_page;
D3(fprintf(debug_file, "GLUE_SEGMENTS = %d pages of %d bytes each\n", GLUE_SEGMENTS, PAGE_PHYS));
for (seg_page = 0; seg_page < SEGMENTS_PAGES + 1; seg_page++){
// allocate SEGMENT_GLUE buffer that will contain data copied from non-pagfe aligned segments
state->writer_params.glue_buffs[seg_page] = (unsigned char *) aligned_alloc (PAGE_PHYS, GLUE_SEGMENTS * PAGE_PHYS);
if (state->writer_params.glue_buffs[seg_page] == NULL) {
params->glue_buffs[seg_page] = (unsigned char *) aligned_alloc (PAGE_PHYS, GLUE_SEGMENTS * PAGE_PHYS);
if (params->glue_buffs[seg_page] == NULL) {
return -1;
}
D6(fprintf(debug_file, "init_align_buffers(), state->writer_params.glue_buffs[%d]=%p @ %07d\n", \
seg_page, state->writer_params.glue_buffs[seg_page], get_fpga_usec(state->fd_fparmsall[0], 0)));
D6(fprintf(debug_file, "init_align_buffers(), params->glue_buffs[%d]=%p @ %07d\n", \
seg_page, params->glue_buffs[seg_page], get_fpga_usec(state->fd_fparmsall[0], 0)));
}
for (seg_page = 0; seg_page < SEGMENTS_PAGES; seg_page++){
state->writer_params.data_segments[seg_page] = (struct iovec *)malloc(SEGMENTS_NUMBER * sizeof(struct iovec));
if (state->writer_params.data_segments[seg_page] == NULL) {
params->data_segments[seg_page] = (struct iovec *)malloc(SEGMENTS_NUMBER * sizeof(struct iovec));
if (params->data_segments[seg_page] == NULL) {
return -1;
}
D6(fprintf(debug_file, "init_align_buffers(), seg_page=%d, state->writer_params.data_segments[seg_page]=%p @ %07d\n", \
seg_page, state->writer_params.data_segments[seg_page], get_fpga_usec(state->fd_fparmsall[0], 0)));
D6(fprintf(debug_file, "init_align_buffers(), seg_page=%d, params->data_segments[seg_page]=%p @ %07d\n", \
seg_page, params->data_segments[seg_page], get_fpga_usec(state->fd_fparmsall[0], 0)));
state->writer_params.data_segments[seg_page][SEGMENT_GLUE].iov_base = state->writer_params.glue_buffs[seg_page];
state->writer_params.data_segments[seg_page][SEGMENT_GLUE].iov_len = 0;
params->data_segments[seg_page][SEGMENT_GLUE].iov_base = params->glue_buffs[seg_page];
params->data_segments[seg_page][SEGMENT_GLUE].iov_len = 0;
}
state->writer_params.glue_carry_vec.iov_base = state->writer_params.glue_buffs[SEGMENTS_PAGES];
state->writer_params.glue_carry_vec.iov_len = 0; // nothing there fro the first frame (TODO: need same for start write?)
params->glue_carry_vec.iov_base = params->glue_buffs[SEGMENTS_PAGES];
params->glue_carry_vec.iov_len = 0; // nothing there fro the first frame (TODO: need same for start write?)
return 0;
}
......@@ -300,25 +307,27 @@ int init_align_buffers(camogm_state *state)
void deinit_align_buffers(camogm_state *state)
{
int seg_page;
struct writer_params *params = &state->writer_params;
for (seg_page = 0; seg_page < SEGMENTS_PAGES+1; seg_page++){
free (state->writer_params.glue_buffs[seg_page]);
state->writer_params.glue_buffs[seg_page] = NULL;
free (params->glue_buffs[seg_page]);
params->glue_buffs[seg_page] = NULL;
}
for (seg_page = 0; seg_page < SEGMENTS_PAGES; seg_page++){
free(state->writer_params.data_segments[seg_page]);
state->writer_params.data_segments[seg_page] = NULL;
free(params->data_segments[seg_page]);
params->data_segments[seg_page] = NULL;
}
}
void reset_segments(camogm_state *state, int all, int page)
{
int i;
struct iovec *vects = state->writer_params.data_segments[page];
struct writer_params *params = &state->writer_params;
struct iovec *vects = params->data_segments[page];
for (i = 0; i < SEGMENTS_NUMBER; i++) {
vects[i].iov_len = 0;
}
if (all) {
state->writer_params.glue_carry_vec.iov_len = 0;
params->glue_carry_vec.iov_len = 0;
}
}
......@@ -328,3 +337,14 @@ uint64_t lba_to_offset(uint64_t lba)
{
return lba * PHY_BLOCK_SIZE;
}
/** Get next LBA after segment prepared for writing. There is no actual
* write pointer as recording can be out of order. When the "file" is closed,
* this pointer matches the normal file pointer, just in blocks.
* @param params record state parameters
* @return full LBA (from the disk start) write pointer, measured in 512-byte sectors.
*/
uint64_t get_lba_next(const struct writer_params *params){
return params->lba_start + (params->next_segment_pos / PHY_BLOCK_SIZE); // next prepared (not yet written)
}
......@@ -49,6 +49,7 @@ void deinit_align_buffers(camogm_state *state);
void reset_segments(camogm_state *state, int all, int page);
size_t remap_vectors(camogm_state *state); //, struct iovec *chunks);
uint64_t lba_to_offset(uint64_t lba);
uint64_t get_lba_next(const struct writer_params *params);
int vectaligntail(struct iovec *dest);
......
......@@ -38,6 +38,11 @@
#include "camogm_read.h"
#include "camogm_align.h"
//#define BUFFER_KVEC
// just for Eclipse to be happy
#ifndef O_DIRECT
#define O_DIRECT __O_DIRECT
#endif
#define MAX_IMAGE_ALIGNED (4096*256)
/** State file record format. It includes device path in /dev, starting, current and ending LBAs */
#define STATE_FILE_FORMAT "%s\t%llu\t%llu\t%llu\n"
......@@ -48,6 +53,56 @@
static void *jpeg_writer(void *thread_args);
static int save_state_file(const camogm_state *state);
/**
* Get next segment page to write to disk - should run under locked mutex
* @param wparams
* @return next page or -1 if none are ready
*/
inline int get_write_page(struct writer_params *wparams){
int i, wpage = wparams->chunk_page_prep;
for (i = 0; i < SEGMENTS_PAGES; i++){
if (wparams->chunk_page_state[wpage] == SEGPAGE_FULL) {
return wpage;
}
wpage++;
if (wpage >= SEGMENTS_PAGES){
wpage -= SEGMENTS_PAGES;
}
}
return -1;
}
/**
* Get number of emty pages for the main thread to fill
* @param state
* @return number of next empty pages
*/
inline int get_num_empty(struct writer_params *wparams){
int i, wpage = wparams->chunk_page_prep;
for (i = 0; i < SEGMENTS_PAGES; i++){
if (wparams->chunk_page_state[wpage] != SEGPAGE_EMPTY) {
return i;
}
wpage++;
if (wpage >= SEGMENTS_PAGES){
wpage -= SEGMENTS_PAGES;
}
}
return SEGMENTS_PAGES;
}
inline int get_num_busy(struct writer_params *wparams){
int i, nbusy = 0;
for (i = 0; i < SEGMENTS_PAGES; i++){
if (wparams->chunk_page_state[i] == SEGPAGE_BUSY) {
nbusy++;
}
}
return nbusy;
}
/**
* Emulate writev() by consecutive write()
* @param __fd
......@@ -120,27 +175,33 @@ static int find_state(FILE *f, uint64_t *pos, const rawdev_buffer *rawdev)
if (f == NULL || pos == NULL)
return -1;
if (get_disk_range(&range) != 0) {
return -1;
return -2;
}
D3(fprintf(debug_file, "get_disk_range() -> from = %llu, to=%llu\n", range.from, range.to));
// skip first line containing file header
fgets(buff, ELPHEL_PATH_MAX, f);
D3(fprintf(debug_file, "find_state(): Got first string '%s' from state file %s\n", buff, rawdev->state_path));
while (fgets(buff, ELPHEL_PATH_MAX, f) != NULL) {
D3(fprintf(debug_file, "find_state(): Got next string '%s' from state file %s\n", buff, rawdev->state_path));
sscanf(buff, STATE_FILE_FORMAT, dev_name, &start_pos, &curr_pos, &end_pos);
len = strlen(dev_name);
if (strncmp(rawdev->rawdev_path, dev_name, len) == 0 &&
range.from == start_pos &&
range.to == end_pos) {
D3(fprintf(debug_file, "find_state():got match, curr_pos= %llu from=%llu, to=%llu\n", curr_pos, range.from, range.to));
*pos = curr_pos;
break;
return 0;
} else {
D3(fprintf(debug_file, "find_state():no match, dev_name=%s(%s), from=%llu(%llu), to=%llu(%llu) curr_pos= %llu\n",\
dev_name, rawdev->rawdev_path, start_pos, range.from, end_pos, range.to, curr_pos));
}
}
return 0;
D3(fprintf(debug_file, "find_state():no match, return -2"));
return -3;
}
/** Read state from file and restore disk write pointer */
int open_state_file(const rawdev_buffer *rawdev, uint64_t *current_pos)
int open_state_file(const rawdev_buffer *rawdev, uint64_t *current_lba)
{
FILE *f;
int ret = 0;
......@@ -152,15 +213,14 @@ int open_state_file(const rawdev_buffer *rawdev, uint64_t *current_pos)
f = fopen(rawdev->state_path, "r");
if (f != NULL) {
if (find_state(f, &lba_pos, rawdev) != -1) {
*current_pos = lba_pos;
D0(fprintf(debug_file, "Got starting LBA from state file: %llu\n", lba_pos));
if ((ret = find_state(f, &lba_pos, rawdev)) == 0) {
*current_lba = lba_pos;
D1(fprintf(debug_file, "Got starting LBA from state file %s: %llu\n", rawdev->state_path, lba_pos));
}
fclose(f);
} else {
ret = -1;
ret = -4;
}
return ret;
}
......@@ -171,7 +231,15 @@ static int save_state_file(const camogm_state *state)
FILE *f;
const rawdev_buffer *rawdev = &state->rawdev;
const struct writer_params *params = &state->writer_params;
uint64_t current_lba = get_lba_next(params);
D3(fprintf(debug_file, "save_state_file(): current_lba=%lld lba_start=%lld, lba_end=%lld @ %07d\n", \
current_lba , params->lba_start, params->lba_end, get_fpga_usec(state->fd_fparmsall[0], 0)));
if ((current_lba < params->lba_start) || (current_lba > params->lba_end)){
D0(fprintf(debug_file, "save_state_file(): ERROR ! invalid current_lba=%lld, params->next_segment_pos=%lld @ %07d\n",\
current_lba, params->next_segment_pos, get_fpga_usec(state->fd_fparmsall[0], 0)));
}
D3(syslog (LOG_INFO, "save_state_file(): lba_start = %lld, lba_end=%lld, current_lba=%lld, params->next_segment_pos=%lld",\
params->lba_start, params->lba_end, get_lba_next(params), params->next_segment_pos));
if (strlen(rawdev->state_path) == 0) {
return ret;
}
......@@ -182,10 +250,13 @@ static int save_state_file(const camogm_state *state)
return -1;
}
fprintf(f, "Device\t\tStart LBA\tCurrent LBA\tEnd LBA\n");
fprintf(f, STATE_FILE_FORMAT, rawdev->rawdev_path, params->lba_start, params->lba_current, params->lba_end);
// fprintf(f, STATE_FILE_FORMAT, rawdev->rawdev_path, params->lba_start, params->lba_current, params->lba_end);
fprintf(f, STATE_FILE_FORMAT, rawdev->rawdev_path, params->lba_start, get_lba_next(params), params->lba_end);
fflush(f);
fsync(fileno(f));
fclose(f);
D6(fprintf(debug_file,"Device\t\tStart LBA\tCurrent LBA\tEnd LBA\n");\
fprintf(f, STATE_FILE_FORMAT, rawdev->rawdev_path, params->lba_start, get_lba_next(params), params->lba_end));
return ret;
}
......@@ -201,29 +272,28 @@ int camogm_init_jpeg(camogm_state *state)
{
int ret = 0;
int ret_val;
if (state->writer_params.state == STATE_STOPPED) {
ret_val = pthread_cond_init(&state->writer_params.main_cond, NULL);
struct writer_params *params = &state->writer_params;
if (params->state == STATE_STOPPED) {
ret_val = pthread_cond_init(&params->main_cond, NULL);
if (ret_val != 0) {
D0(fprintf(debug_file, "Can not initialize conditional variable for main thread: %s\n", strerror(ret_val)));
ret = -1;
}
ret_val = pthread_cond_init(&state->writer_params.writer_cond, NULL);
ret_val = pthread_cond_init(&params->writer_cond, NULL);
if (ret_val != 0) {
D0(fprintf(debug_file, "Can not initialize conditional variable for writing thread: %s\n", strerror(ret_val)));
ret = -1;
}
ret_val = pthread_mutex_init(&state->writer_params.writer_mutex, NULL);
ret_val = pthread_mutex_init(&params->writer_mutex, NULL);
if (ret_val != 0) {
D0(fprintf(debug_file, "Can not initialize mutex for writing thread: %s\n", strerror(ret_val)));
ret = -1;
}
ret_val = pthread_create(&state->writer_params.writer_thread, NULL, jpeg_writer, (void *)state);
// ret_val = pthread_create(&params->writer_thread, NULL, jpeg_writer, (void *)state);
if (ret_val != 0) {
D0(fprintf(debug_file, "Can not start writer thread: %s\n", strerror(ret_val)));
ret = -1;
}
ret_val = init_align_buffers(state);
if (ret_val != 0) {
D0(fprintf(debug_file, "Can not initialize alignment buffers\n"));
......@@ -242,18 +312,25 @@ int camogm_init_jpeg(camogm_state *state)
*/
void camogm_free_jpeg(camogm_state *state)
{
pthread_cond_destroy(&state->writer_params.main_cond);
pthread_cond_destroy(&state->writer_params.writer_cond);
pthread_mutex_destroy(&state->writer_params.writer_mutex);
// terminate writing thread
pthread_mutex_lock(&state->writer_params.writer_mutex);
state->writer_params.exit_thread = true;
pthread_cond_signal(&state->writer_params.writer_cond);
pthread_mutex_unlock(&state->writer_params.writer_mutex);
pthread_join(state->writer_params.writer_thread, NULL);
state->writer_params.exit_thread = false;
struct writer_params *params = &state->writer_params;
pthread_cond_destroy(&params->main_cond);
pthread_cond_destroy(&params->writer_cond);
pthread_mutex_destroy(&params->writer_mutex);
// terminate writing threads
#if 0
pthread_mutex_lock(&params->writer_mutex);
params->exit_write_threads = true;
pthread_cond_broadcast(&params->writer_cond);
pthread_mutex_unlock(&params->writer_mutex);
for (int i = 0; i < NUM_WRITER_THREADS; i++){
if (params->writer_threads[i]) {
pthread_join(params->writer_threads[i], NULL);
}
}
#endif
reset_segments(state, 1, params->chunk_page_prep);
params->exit_write_threads = false;
deinit_align_buffers(state);
}
......@@ -280,9 +357,11 @@ int64_t camogm_get_jpeg_size(camogm_state *state)
int camogm_start_jpeg(camogm_state *state)
{
char * slash;
int rslt;
uint64_t offset;
int rslt; // , i;
// uint64_t offset;
struct writer_params *params = &state->writer_params;
uint64_t current_lba = params->lba_start;
params->circbuf_many = false;
if (!state->rawdev_op) {
strcpy(state->path, state->path_prefix); // make state->path a directory name (will be replaced when the frames will be written)
slash = strrchr(state->path, '/');
......@@ -299,24 +378,53 @@ int camogm_start_jpeg(camogm_state *state)
}
}
} else {
if (open_state_file(&state->rawdev, &state->writer_params.lba_current) != 0) {
D0(fprintf(debug_file, "Could not get write pointer from state file, recording will start from the beginning of partition: "
"%s\n", state->rawdev.rawdev_path));
#if 0
state->writer_params.chunk_page_prep = 0; // was in init. Or should it continue and rely thatall prepared should be cleared when closing file
for (i = 0; i < SEGMENTS_PAGES; i++){
state->writer_params.chunk_page_state[i] = SEGPAGE_EMPTY;
}
// state->writer_params.blockdev_fd = open(state->rawdev.rawdev_path, O_RDWR | 0x40000); // __O_DIRECT); // __O_DIRECT);
state->writer_params.blockdev_fd = open(state->rawdev.rawdev_path, O_RDWR | O_DIRECT); // __O_DIRECT); // __O_DIRECT);
// int mode = fcntl(fd, F_GETFL);
D0(syslog (LOG_INFO, "Raw %s is opened in 0x%08x mode, __O_DIRECT=0x%08x, O_DIRECT=0x%08x", state->rawdev.rawdev_path, fcntl(state->writer_params.blockdev_fd, F_GETFL), __O_DIRECT, O_DIRECT));
if (state->writer_params.blockdev_fd < 0) {
D0(fprintf(debug_file, "Error opening block device: %s\n", state->rawdev.rawdev_path));
return -CAMOGM_FRAME_FILE_ERR;
state->writer_params.writev_run = false;
#endif
D6(fprintf(debug_file, "start_jpeg() before open_state_file: Block device positions: start = %llu, current = %llu, end = %llu, params->next_segment_pos=0x%llx\n",
state->writer_params.lba_start, current_lba, state->writer_params.lba_end, state->writer_params.next_segment_pos));
if ((rslt=open_state_file(&state->rawdev, &current_lba)) != 0){ // }&params->lba_current) != 0) {
D0(fprintf(debug_file, "Could not get write pointer from state file (err=%d), recording will start from the rounded to page beginning of partition: "
"%s\n", rslt, state->rawdev.rawdev_path));
}
offset = lba_to_offset(state->writer_params.lba_current - state->writer_params.lba_start);
lseek64(state->writer_params.blockdev_fd, offset, SEEK_SET);
D3(fprintf(debug_file, "Open block device: %s, offset in bytes: %llu\n", state->rawdev.rawdev_path, offset));
state->writer_params.stat_update = time(NULL);
}
/// params->next_segment_pos = lba_to_offset(params->lba_current - params->lba_start);
if ((current_lba < state->writer_params.lba_start) || (current_lba > state->writer_params.lba_end)) {
D0(fprintf(debug_file, "start_jpeg(): INVALID start lba: start = %llu, current = %llu, end = %llu, resetting current_lba to lba_start\n",
state->writer_params.lba_start, current_lba, state->writer_params.lba_end)); // incorrect
current_lba=state->writer_params.lba_start;
}
state->writer_params.next_segment_pos = lba_to_offset(current_lba - state->writer_params.lba_start);
D3(fprintf(debug_file, "start_jpeg(): Block device positions: start = %llu, current = %llu, end = %llu, params->next_segment_pos=0x%llx\n",
state->writer_params.lba_start, current_lba, state->writer_params.lba_end, state->writer_params.next_segment_pos)); // incorrect
params->next_segment_pos = lba_to_offset(current_lba - params->lba_start);
D3(syslog (LOG_INFO, "start_jpeg(): lba_start = %lld, lba_end=%lld, current_lba=%lld",\
params->lba_start, params->lba_end, get_lba_next(params)));
D2(fprintf(debug_file, "First write to block device: %s, will start with offset in bytes: %llu\n", \
state->rawdev.rawdev_path, params->next_segment_pos));
for (int i = 0; i < NUM_WRITER_THREADS; i++){
params->writer_threads[i] = 0;
}
params->exit_write_threads = false;
for (int i = 0; i < NUM_WRITER_THREADS; i++){
rslt = pthread_create(&params->writer_threads[i], NULL, jpeg_writer, (void *)state);
if (rslt != 0) {
D0(fprintf(debug_file, "Can not start writer threads[%d]: %s\n", i, strerror(rslt)));
return -CAMOGM_FRAME_FILE_ERR;
}
D6(fprintf(debug_file, "Started writer thread [%d] id=%ld OK\n", i, params->writer_threads[i]));
}
params->stat_update = get_fpga_time64(state->fd_fparmsall[0], 0);
D1(fprintf(debug_file, "Started %d writer threads\n", NUM_WRITER_THREADS));
// check if immediately exited because of open error? if (params->exit_write_threads) - now and while waiting for free buffer ?
}
return 0;
}
......@@ -330,11 +438,16 @@ int camogm_start_jpeg(camogm_state *state)
int camogm_frame_jpeg(camogm_state *state)
{
int i, j;
int dbg_start_wait = 0, dbg_wait;
ssize_t iovlen, l = 0;
struct iovec chunks_iovec[8];
int port = state->port_num;
time_t curr_time;
// time_t curr_time;
uint64_t curr_time;
int is_tiff = state->this_frame_params[port].color == COLORMODE_RAW;
struct writer_params *params = &state->writer_params;
int seg_page = params->chunk_page_prep;
uint64_t disk_size = (params->lba_end - params->lba_start) * PHY_BLOCK_SIZE;
if (is_tiff) {
sprintf(state->path, "%s%d_%010ld_%06ld.tiff", state->path_prefix, port, state->this_frame_params[port].timestamp_sec, state->this_frame_params[port].timestamp_usec);
} else {
......@@ -361,51 +474,121 @@ int camogm_frame_jpeg(camogm_state *state)
state->rawdev.last_jpeg_size = l;
close(state->ivf);
} else {
D6(fprintf(debug_file, "ccam_dma_buf[%d] = %p\n",port, get_ccam_dma_buf(port)));
D3(fprintf(debug_file, "_12.7_ camogm_frame_jpeg(): %ld.%06ld:%d @ %07d\n",state->this_frame_params[port].timestamp_sec, state->this_frame_params[port].timestamp_usec, port, get_fpga_usec(state->fd_fparmsall[0], 0)));
D3(fprintf(debug_file, "\n"));
D4(fprintf(debug_file, "_12.7_ camogm_frame_jpeg(): %ld.%06ld:%d @ %07d\n",state->this_frame_params[port].timestamp_sec, state->this_frame_params[port].timestamp_usec, port, get_fpga_usec(state->fd_fparmsall[0], 0)));
D6(fprintf(debug_file, "\ndump iovect array for port %u, chunk_index=%u, chunk_data_index=%u, data_segments=%u\n", state->port_num, state->chunk_index, state->chunk_data_index, state->data_segments));
for (int i = 0; i < state->chunk_index; i++) {
D6(fprintf(debug_file, "ptr: %p, length: %ld\n", state->packetchunks[i].chunk, state->packetchunks[i].bytes));
}
// next frame is ready for recording, signal this to the writer thread
D6(fprintf(debug_file, "_13_: Yielding thread before getting a lock (%d, %d):%d @ %07d\n", state->writer_params.chunk_page_prep, state->writer_params.chunk_page_write, state->writer_params.writev_run, get_fpga_usec(state->fd_fparmsall[0], 0)));
D6(fprintf(debug_file, "_13_: Yielding thread before getting a lock (%d):[%d%d:%d%d] @ %07d\n",\
params->chunk_page_prep, params->writev_run[0], params->writev_run[1], \
params->write_waits_sig[0], params->write_waits_sig[1], get_fpga_usec(state->fd_fparmsall[0], 0)));
pthread_yield();
pthread_mutex_lock(&state->writer_params.writer_mutex);
D6(fprintf(debug_file, "_13a_: waiting for empty buffer (%d, %d):%d @ %07d\n", state->writer_params.chunk_page_prep, state->writer_params.chunk_page_write, state->writer_params.writev_run, get_fpga_usec(state->fd_fparmsall[0], 0)));
pthread_mutex_lock(&params->writer_mutex);
/// params->circbuf_many = get_num_empty(params) >= NUM_NEED_NEMPTY;
if (get_num_empty(params) < NUM_FREE_MANY) {
params->circbuf_many = true;
}
D3(dbg_start_wait = get_fpga_usec(state->fd_fparmsall[0], 0); fprintf(debug_file, \
"_13a_: waiting for empty buffer (%d): [%d%d%d%d%d%d%d%d] LOCK [%d%d:%d%d:%d%d] @ %07d\n",\
params->chunk_page_prep, \
params->chunk_page_state[0],params->chunk_page_state[1],params->chunk_page_state[2],params->chunk_page_state[3], \
params->chunk_page_state[4],params->chunk_page_state[5],params->chunk_page_state[6],params->chunk_page_state[7], \
params->writev_run[0], params->writev_run[1], \
params->write_waits_sig[0], params->write_waits_sig[1], \
params->write_go[0], params->write_go[1], \
dbg_start_wait));
// do not run ahead by >= FILE_CHUNKS_PAGES (0 or 1 for just FILE_CHUNKS_PAGES == 2)
// either buffer is completely empty (state->writer_params->chunk_page_prep == state->writer_params->chunk_page_write)
// or it is not full and writev_run is active
while (((state->writer_params.chunk_page_prep - state->writer_params.chunk_page_write + (2 * SEGMENTS_PAGES))
% (2 * SEGMENTS_PAGES) >= SEGMENTS_PAGES) ||
(!state->writer_params.writev_run && (state->writer_params.chunk_page_prep != state->writer_params.chunk_page_write))) {
D6(fprintf(debug_file, "_13b_: waiting for empty buffer (%d, %d):%d @ %07d\n", state->writer_params.chunk_page_prep, state->writer_params.chunk_page_write, state->writer_params.writev_run, get_fpga_usec(state->fd_fparmsall[0], 0)));
pthread_cond_wait(&state->writer_params.main_cond, &state->writer_params.writer_mutex);
while (((i = get_num_empty(params)) < NUM_NEED_EMPTY) && !params->exit_write_threads) { // get_num_empty(params)< 3?
D6(fprintf(debug_file, "_13b_: waiting for empty buffer (%d):[%d%d:%d%d] @ %07d\n",\
params->chunk_page_prep, params->writev_run[0], params->writev_run[1], \
params->write_waits_sig[0], params->write_waits_sig[1], get_fpga_usec(state->fd_fparmsall[0], 0)));
pthread_cond_wait(&params->main_cond, &params->writer_mutex);
} // pthread_cond_signal, pthread_cond_broadcast - signal or broadcast a condition
pthread_mutex_unlock(&params->writer_mutex);
D3(dbg_wait=get_fpga_usec(state->fd_fparmsall[0], 0) - dbg_start_wait; if (dbg_wait <0) dbg_wait+=10000000;
fprintf(debug_file, "_13b0_: got empty buffer at %d (%d pages): [%d%d%d%d%d%d%d%d] [%d%d:%d%d:%d%d] @ %07d (+%07d)\n",\
params->chunk_page_prep, i, \
params->chunk_page_state[0],params->chunk_page_state[1],params->chunk_page_state[2],params->chunk_page_state[3], \
params->chunk_page_state[4],params->chunk_page_state[5],params->chunk_page_state[6],params->chunk_page_state[7], \
params->writev_run[0], params->writev_run[1], \
params->write_waits_sig[0], params->write_waits_sig[1], \
params->write_go[0], params->write_go[1], \
get_fpga_usec(state->fd_fparmsall[0], 0), dbg_wait));
if (params->exit_write_threads) { // at least one of the threads abnormally exited
D0(fprintf(debug_file, "_13b1_:exit_write_threads is true, abnormally exiting threads @ %07d\n",get_fpga_usec(state->fd_fparmsall[0], 0)));
for (int i = 0; i < NUM_WRITER_THREADS; i++){
if (params->writer_threads[i]) {
D0(fprintf(debug_file, "_13b2_:exiting writer thread % d @ %07d\n", i, get_fpga_usec(state->fd_fparmsall[0], 0)));
pthread_join(params->writer_threads[i], NULL);
params->writer_threads[i] = 0;
}
}
D0(fprintf(debug_file, "_13b3_:all writer threads exited @ %07d\n",get_fpga_usec(state->fd_fparmsall[0], 0)));
return -CAMOGM_FRAME_FILE_ERR;
}
pthread_mutex_unlock(&state->writer_params.writer_mutex);
D6(fprintf(debug_file, "_13c_: got empty buffer (%d, %d):%d @ %07d\n", state->writer_params.chunk_page_prep, state->writer_params.chunk_page_write, state->writer_params.writev_run, get_fpga_usec(state->fd_fparmsall[0], 0)));
D6(fprintf(debug_file, "_13c_: got empty buffer (%d):[%d%d:%d%d] @ %07d\n",\
params->chunk_page_prep, params->writev_run[0], params->writev_run[1], \
params->write_waits_sig[0], params->write_waits_sig[1], get_fpga_usec(state->fd_fparmsall[0], 0)));
// params->dbg_data[params->chunk_page_prep][0] = port; // inside remap_vectors(state)
remap_vectors(state); // now can be used instead of align_frame(state), returns total length
// align_frame(state);
// save segment file position, update next
params->segment_pos[seg_page] = params->next_segment_pos;
for (i = 0; i < SEGMENTS_NUMBER; i++){
params->next_segment_pos += params->data_segments[seg_page][i].iov_len;
}
// wrap over file size
if (params->next_segment_pos >= disk_size){
params->next_segment_pos -= disk_size;
}
// proceed if last frame was recorded without errors
D6(fprintf(debug_file, "_13d_:frame aligned @ %07d\n",get_fpga_usec(state->fd_fparmsall[0], 0)));
pthread_mutex_lock(&state->writer_params.writer_mutex);
if (state->writer_params.last_ret_val == 0) {
state->writer_params.chunk_page_prep = (state->writer_params.chunk_page_prep + 1) % (2 * SEGMENTS_PAGES);
// state->writer_params.data_ready = true;
pthread_cond_signal(&state->writer_params.writer_cond);
D6(fprintf(debug_file, "_13e_: chunk_page_prep = %d (%d), signal writer_cond @ %07d\n",state->writer_params.chunk_page_prep, state->writer_params.chunk_page_write, get_fpga_usec(state->fd_fparmsall[0], 0)));
pthread_mutex_lock(&params->writer_mutex);
if (params->last_ret_val == 0) {
params->chunk_page_state[params->chunk_page_prep] = SEGPAGE_FULL; // mark as full;
params->chunk_page_prep = (params->chunk_page_prep + 1) % SEGMENTS_PAGES; // ***************** Here NEXT SEGMENT
if (params->write_waits_sig[0] || params->write_waits_sig[1]) {
pthread_cond_broadcast(&params->writer_cond);
D3(fprintf(debug_file, "_13e_: chunk_page_prep = %d, signaled writer_cond * [%d%d:%d%d:%d%d] @ %07d\n", \
params->chunk_page_prep, params->writev_run[0], params->writev_run[1], \
params->write_waits_sig[0], params->write_waits_sig[1], \
params->write_go[0], params->write_go[1], \
get_fpga_usec(state->fd_fparmsall[0], 0)));
} else {
D3(fprintf(debug_file, "_13e0_: chunk_page_prep = %d, nobody listens, no signal -[%d%d:%d%d:%d%d] @ %07d\n", \
params->chunk_page_prep, params->writev_run[0], params->writev_run[1], \
params->write_waits_sig[0], params->write_waits_sig[1], \
params->write_go[0], params->write_go[1], \
get_fpga_usec(state->fd_fparmsall[0], 0)));
}
}
pthread_mutex_unlock(&state->writer_params.writer_mutex);
if (state->writer_params.last_ret_val != 0) {
return state->writer_params.last_ret_val;
pthread_mutex_unlock(&params->writer_mutex);
// add yield here?
D4(fprintf(debug_file, "_13e2a_ @ %07d\n", get_fpga_usec(state->fd_fparmsall[0], 0)));
if (params->last_ret_val != 0) {
return params->last_ret_val;
}
D4(fprintf(debug_file, "_13e2b_ @ %07d\n", get_fpga_usec(state->fd_fparmsall[0], 0)));
// update status file if time has come
curr_time = time(NULL);
if (difftime(curr_time, state->writer_params.stat_update) > STAT_UPDATE_PERIOD) {
// curr_time = time(NULL);
curr_time = get_fpga_time64(state->fd_fparmsall[0], 0);
// if (difftime(curr_time, params->stat_update) > STAT_UPDATE_PERIOD) {
if ((curr_time - params->stat_update) > (STAT_UPDATE_PERIOD * 1000000)) { // STAT_UPDATE_PERIOD is in seconds
save_state_file(state);
D6(fprintf(debug_file, "_13f_: saved state file @ %07d\n", get_fpga_usec(state->fd_fparmsall[0], 0)));
params->stat_update = curr_time;
D3(fprintf(debug_file, "_13f_: saved state file @ %07d\n", get_fpga_usec(state->fd_fparmsall[0], 0)));
}
}
D4(fprintf(debug_file, "_13e2_ camogm_frame_jpeg-> @ %07d\n", get_fpga_usec(state->fd_fparmsall[0], 0)));
return 0;
}
......@@ -416,50 +599,76 @@ int camogm_frame_jpeg(camogm_state *state)
* @param state a pointer to a structure containing current state
* @return 0 if the device was closed successfully and -1 otherwise
*/
int camogm_end_jpeg(camogm_state *state) // TODO: Resolve multippage !
int camogm_end_jpeg(camogm_state *state)
{
int ret = 0;
int bytes, ceil_size;
ssize_t iovlen;
struct writer_params *params = &state->writer_params;
int seg_page = params->chunk_page_prep;
uint64_t disk_size = (params->lba_end - params->lba_start) * PHY_BLOCK_SIZE;
if (state->rawdev_op) {
// write any remaining data, do not use writer thread as there can be only one block left CHUNK_REM buffer
pthread_mutex_lock(&state->writer_params.writer_mutex);
while (state->writer_params.chunk_page_prep != state->writer_params.chunk_page_write)
// wait for previous frame to be recorded first if it has not be recorded by the moment
pthread_cond_wait(&state->writer_params.main_cond, &state->writer_params.writer_mutex);
bytes = state->writer_params.glue_carry_vec.iov_len;
bytes = params->glue_carry_vec.iov_len; // is not modified by writer threads
if (bytes > 0) {
ceil_size = vectaligntail(&state->writer_params.glue_carry_vec);
ceil_size = vectaligntail(&params->glue_carry_vec);
D1(fprintf(debug_file, "Write last block of data, size = %d, full size = %d\n", bytes, ceil_size));
// the remaining data block is placed in CHUNK_COMMON buffer, write just this buffer
iovlen = emul_writev(state->writer_params.blockdev_fd,
&state->writer_params.glue_carry_vec,
1);
if (iovlen < bytes) {
D0(fprintf(debug_file, "emul_writev error: %s (returned %i, expected %i)\n", strerror(errno), iovlen, bytes));
state->writer_params.last_ret_val = -CAMOGM_FRAME_FILE_ERR;
} else {
// update statistic, just one block written
state->writer_params.lba_current += 1;
state->rawdev.total_rec_len += bytes;
pthread_mutex_lock(&params->writer_mutex);
D6(fprintf(debug_file, "_x13a_: waiting for empty buffer (%d):[%d%d:%d%d] @ %07d\n",\
params->chunk_page_prep, params->writev_run[0], params->writev_run[1], \
params->write_waits_sig[0], params->write_waits_sig[1], get_fpga_usec(state->fd_fparmsall[0], 0)));
// Wait for the segment buffer (same as for the next frame)
while ((params->chunk_page_state[params->chunk_page_prep] != SEGPAGE_EMPTY) || // next page is busy, can not go ahead
// (!params->writev_run && (get_write_page(params) >= 0))) {
// TODO: Update?
(!params->writev_run[0] && !params->writev_run[1] && (get_write_page(params) >= 0))) {
D6(fprintf(debug_file, "_x13b_: waiting for empty buffer (%d):[%d%d:%d%d] @ %07d\n", \
params->chunk_page_prep, params->writev_run[0], params->writev_run[1], \
params->write_waits_sig[0], params->write_waits_sig[1], get_fpga_usec(state->fd_fparmsall[0], 0)));
pthread_cond_wait(&params->main_cond, &params->writer_mutex);
}
pthread_mutex_unlock(&params->writer_mutex);
// prepare last segment, increment segment page
params->data_segments[seg_page][SEGMENT_GLUE].iov_base = params->glue_carry_vec.iov_base;
params->data_segments[seg_page][SEGMENT_GLUE].iov_len = params->glue_carry_vec.iov_len;
params->data_segments[seg_page][SEGMENT_FIRST].iov_len = 0; // check iov_len first before using iov_base
params->data_segments[seg_page][SEGMENT_SECOND].iov_len = 0; // check iov_len first before using iov_base
params->segment_pos[seg_page] = params->next_segment_pos;
params->next_segment_pos += params->data_segments[seg_page][SEGMENT_GLUE].iov_len;
// wrap over file size
if (params->next_segment_pos >= disk_size){
params->next_segment_pos -= disk_size;
}
D6(fprintf(debug_file, "_x13d_:frame aligned @ %07d\n",get_fpga_usec(state->fd_fparmsall[0], 0)));
pthread_mutex_lock(&params->writer_mutex);
params->chunk_page_state[params->chunk_page_prep] = SEGPAGE_FULL;
params->chunk_page_prep = (params->chunk_page_prep + 1) % SEGMENTS_PAGES; // ***************** Here NEXT SEGMENT (last time)
params->exit_write_threads = true; // finish job and exit
pthread_cond_broadcast(&params->writer_cond);
D6(fprintf(debug_file, "_13e1_: chunk_page_prep = %d, signal writer_cond @ %07d\n",params->chunk_page_prep, get_fpga_usec(state->fd_fparmsall[0], 0)));
pthread_mutex_unlock(&params->writer_mutex);
} // if (bytes > 0) {
D2(fprintf(debug_file, "_x13f0_: will run save_state_file @ %07d\n", get_fpga_usec(state->fd_fparmsall[0], 0)));
save_state_file(state);
params->stat_update = get_fpga_time64(state->fd_fparmsall[0], 0);
D2(fprintf(debug_file, "_x13f_: saved state file after camogm_end_jpeg() @ %07d\n", get_fpga_usec(state->fd_fparmsall[0], 0)));
// terminate writer threads (consider moving create/join threads into initialization/destruction
pthread_mutex_lock(&params->writer_mutex);
params->exit_write_threads = true;
pthread_cond_broadcast(&params->writer_cond);
pthread_mutex_unlock(&params->writer_mutex);
for (int i = 0; i < NUM_WRITER_THREADS; i++){
if (params->writer_threads[i]) {
pthread_join(params->writer_threads[i], NULL);
}
// reset_chunks(state->writer_params.data_chunks, 1);
//*************** See if another buffer(s) must be cleared too? or even use chunk_page_write
reset_segments(state, 1, state->writer_params.chunk_page_prep % SEGMENTS_PAGES);
}
pthread_mutex_unlock(&state->writer_params.writer_mutex);
D2(fprintf(debug_file, "_x13g_: joined writer threads @ %07d\n", get_fpga_usec(state->fd_fparmsall[0], 0)));
D6(fprintf(debug_file, "Closing block device %s\n", state->rawdev.rawdev_path));
ret = close(state->writer_params.blockdev_fd);
if (ret == -1)
D0(fprintf(debug_file, "Error: %s\n", strerror(errno)));
save_state_file(state);
}
} // (state->rawdev_op) {
return ret;
}
#define NUM_TRIES 10
/**
* @brief Disk writing thread. This thread holds local copy of a structure containing current state
* of the program and updates it on a signal from main thread every time new frame is ready for recording.
......@@ -468,45 +677,146 @@ int camogm_end_jpeg(camogm_state *state) // TODO: Resolve multippage !
*/
void *jpeg_writer(void *thread_args)
{
int dbg_start_wait = 0, dbg_wait, dbg_time;
char * dbg_pref;
struct iovec write_segments[SEGMENTS_NUMBER];// 3
int num_write_segments, seg_len;
int dbg_us1=0, dbg_us2=0, dbg_us; // save usec for difference
long dbg_pos;
pthread_t this_thread = pthread_self();
int this_thread_index=-1;
long long dbg_pos;
int reset_rem;
int i, i0, i1;
ssize_t iovlen, l, ll, l0, l1, blocks_write, wpage;
bool process = true, has_data_to_write = false;
long long dbg_pos64;
int i, i0, i1, wpage;
// int32_t res32;
// int64_t res64;
// int errno32, errno64;
ssize_t iovlen, l, ll, l0, l1, blocks_write; // , wpage;
ssize_t writev_wlen;
struct iovec split_chunk;
int blockdev_fd; ///< file descriptor for open block device where frame will be recorded, local for the thread
camogm_state *state = (camogm_state *)thread_args;
struct writer_params *params = &state->writer_params;
params->state = STATE_RUNNING;
D6(fprintf(debug_file, "_w000_: Before jpeg_writer loop @ %07d\n", get_fpga_usec(state->fd_fparmsall[0], 0))); // before the debug file is set !
while (process) {
// find this thread index. Probably no retries are needed
for (i0 = 0; i0 < NUM_TRIES; i0++){
for (i = 0; i < NUM_WRITER_THREADS; i++){
if (pthread_equal(params->writer_threads[i],this_thread)) {
// if (params->writer_threads[i] == this_thread) {
this_thread_index = i;
break;
}
}
if (this_thread_index >=0) {
break;
}
pthread_yield();
}
if (this_thread_index < 0){ // should not happen
D0(syslog (LOG_INFO, "ERROR: could not get thread index in %d tries: thread ID=%ld @ %07d", \
NUM_TRIES, pthread_self(), get_fpga_usec(state->fd_fparmsall[0], 0)));
D0(fprintf(debug_file, "ERROR: could not get thread index in %d tries: thread ID=%ld @ %07d\n", \
NUM_TRIES, pthread_self(), get_fpga_usec(state->fd_fparmsall[0], 0)));
this_thread_index=0;
}
params->write_go[this_thread_index] = false;
dbg_pref = this_thread_index ? " ":" ";
D2( fprintf(debug_file, "%sBefore open [%d%d%d%d%d%d%d%d], page_prep=%d, thread ID=%ld, thread index=%d\n", dbg_pref,\
params->chunk_page_state[0],params->chunk_page_state[1],params->chunk_page_state[2],params->chunk_page_state[3], \
params->chunk_page_state[4],params->chunk_page_state[5],params->chunk_page_state[6],params->chunk_page_state[7], \
params->chunk_page_prep, pthread_self(), this_thread_index));
blockdev_fd = open(state->rawdev.rawdev_path, O_RDWR | O_DIRECT);
D1(syslog (LOG_INFO, "%sRaw %s is opened in 0x%08x mode, thread ID=%ld @ %07d", dbg_pref,\
state->rawdev.rawdev_path, fcntl(blockdev_fd, F_GETFL), pthread_self(), get_fpga_usec(state->fd_fparmsall[0], 0)));
D1(fprintf(debug_file, "%sRaw %s is opened in 0x%08x mode, thread ID=%ld @ %07d\n", dbg_pref,\
state->rawdev.rawdev_path, fcntl(blockdev_fd, F_GETFL), pthread_self(), get_fpga_usec(state->fd_fparmsall[0], 0)));
pthread_mutex_lock(&params->writer_mutex);
D2( fprintf(debug_file, "%s[%d %d %d %d %d %d %d %d], page_prep=%d, thread ID=%ld, lba_end=%lld\n", dbg_pref,\
params->chunk_page_state[0],params->chunk_page_state[1],params->chunk_page_state[2],params->chunk_page_state[3], \
params->chunk_page_state[4],params->chunk_page_state[5],params->chunk_page_state[6],params->chunk_page_state[7], \
params->chunk_page_prep, pthread_self(), params->lba_end));
pthread_mutex_unlock(&params->writer_mutex);
//pthread_self()
if (blockdev_fd < 0) {
D0(fprintf(debug_file, "%sError opening block device: %s, exiting thread\n", dbg_pref, state->rawdev.rawdev_path));
pthread_cond_signal(&params->main_cond); // wake up main thread
params->exit_write_threads = true;
return NULL; //
}
//strerror(errno),
params->state = STATE_RUNNING; // TODO: Check meaning with multiple write threads
pthread_yield();
D2(fprintf(debug_file, "%s_w000_: This thread index =%d (got in i0=%d) Before jpeg_writer loop @ %07d\n", dbg_pref,\
this_thread_index,i0, get_fpga_usec(state->fd_fparmsall[0], 0))); // before the debug file is set !
while (true) { // will break if no data and params->exit_write_threads
pthread_mutex_lock(&params->writer_mutex); // first time before the debug file is set !
D6(fprintf(debug_file, "_w00_: (%d, %d) @ %07d\n", params->chunk_page_prep, params->chunk_page_write, get_fpga_usec(state->fd_fparmsall[0], 0)));
while ((params->chunk_page_prep == params->chunk_page_write) && !params->exit_thread) {
pthread_cond_wait(&params->writer_cond, &params->writer_mutex);
params->write_go[this_thread_index] = false;
params->write_waits_sig[this_thread_index] = true;
D3(dbg_start_wait = get_fpga_usec(state->fd_fparmsall[0], 0); \
fprintf(debug_file, "%s_w00_: (%d) thread ID=%ld got LOCK [%d%d] @ %07d\n", dbg_pref,\
params->chunk_page_prep, pthread_self(),
params->write_waits_sig[0], params->write_waits_sig[1], dbg_start_wait));
// will not start if there are less than 3 (num threads+1) ready pages so the latest one that have DMA open
// will not be touched (its header can return zeros)
// finishing off the remaining pages (slow) is done when no other write threads may be still running DMA
while ((((wpage = get_write_page(params)) < 0) ||
(params->circbuf_many ?
((i = get_num_empty(params)) >= (SEGMENTS_PAGES - NUM_WRITER_THREADS)) : // FULL+BYSY >= (threads+1)
((i = get_num_busy(params)) > 0) ) || // one-by-one
(!params->write_waits_sig[1 - this_thread_index] && !params->write_go[this_thread_index]) // other write thread is not waiting, not already set go
) && !params->exit_write_threads) {
// Wake up main thread while waiting
#if 1
if (get_num_empty(params) >= (NUM_NEED_EMPTY + 2)){
pthread_cond_signal(&params->main_cond); // signal to main thread that prepares data
D3(fprintf(debug_file, "%s_w00.2_: signaling main while waiting to start write [%d%d:%d%d:%d%d] @ %07d\n", \
dbg_pref, \
params->writev_run[0], params->writev_run[1], \
params->write_waits_sig[0], params->write_waits_sig[1], \
params->write_go[0], params->write_go[1], \
get_fpga_usec(state->fd_fparmsall[0], 0)));
}
#endif
pthread_cond_wait(&params->writer_cond, &params->writer_mutex); // They are called with mutex locked by the calling thread or undefined behaviour will result.
}
if (params->exit_thread) {
process = false;
D3(dbg_time= get_fpga_usec(state->fd_fparmsall[0], 0); dbg_wait= dbg_time - dbg_start_wait; if (dbg_wait <0) dbg_wait+=10000000; \
fprintf(debug_file, "%s_w00.3_: (%d, %d), empty/busy:%d many=%d thread ID=%ld got S+D [%d%d:%d%d:%d%d] @ %07d (+%07d)\n", dbg_pref,\
params->chunk_page_prep, wpage, i, params->circbuf_many, pthread_self(), \
params->writev_run[0], params->writev_run[1], \
params->write_waits_sig[0], params->write_waits_sig[1], \
params->write_go[0], params->write_go[1], \
dbg_time, dbg_wait)); // dbg_time
if (params->write_waits_sig[1 - this_thread_index]) { // still waiting?
params->write_go[1 - this_thread_index] = true;
pthread_cond_broadcast(&params->writer_cond); // signal to other writer thread waiting for this one to finish writing
}
params->write_go[this_thread_index] = false;
params->write_waits_sig[this_thread_index] = false;
if (wpage>=0) {
params->chunk_page_state[wpage] = SEGPAGE_BUSY;
}
has_data_to_write = params->chunk_page_prep != params->chunk_page_write;
pthread_mutex_unlock(&params->writer_mutex);
D6(fprintf(debug_file, "_w00.5_: (%d, %d) @ %07d\n", params->chunk_page_prep, params->chunk_page_write,get_fpga_usec(state->fd_fparmsall[0], 0)));
if (has_data_to_write) {
D6(fprintf(debug_file, "_w01_: got write data (%d > %d) @ %07d\n", params->chunk_page_prep, params->chunk_page_write,get_fpga_usec(state->fd_fparmsall[0], 0)));
// does it switch to main right here after unlock?
D3(fprintf(debug_file, "%s_w00.5_: (%d, %d) thread ID=%ld @ %07d\n", dbg_pref,\
params->chunk_page_prep, wpage, pthread_self(), get_fpga_usec(state->fd_fparmsall[0], 0)));
if (wpage >= 0) {
D6(fprintf(debug_file, "%s_w01_: got write segment page %d (preparing %d) @ %07d\n", dbg_pref,\
wpage, params->chunk_page_prep, get_fpga_usec(state->fd_fparmsall[0], 0)));
l = 0;
reset_rem = 0;
state->writer_params.last_ret_val = 0;
// inputs from state->writer_params.data_chunks, returns number of result chunks
wpage = state->writer_params.chunk_page_write % SEGMENTS_PAGES;
params->last_ret_val = 0;
// inputs from params->data_chunks, returns number of result chunks
num_write_segments = 0;
for (i = 0; i < SEGMENTS_NUMBER; i++){
seg_len = state->writer_params.data_segments[wpage][i].iov_len;
seg_len = params->data_segments[wpage][i].iov_len;
if (seg_len > 0){
write_segments[num_write_segments++] = state->writer_params.data_segments[wpage][i];
write_segments[num_write_segments++] = params->data_segments[wpage][i];
l+=seg_len;
}
}
......@@ -515,33 +825,99 @@ void *jpeg_writer(void *thread_args)
if (num_write_segments > 0) {
// Moved rollover here
blocks_write = l / PHY_BLOCK_SIZE; // here LBA, 512
if (state->writer_params.lba_current + blocks_write <= state->writer_params.lba_end) { // almost always - single write
state->writer_params.lba_current += blocks_write;
if (get_lba_next(params) + blocks_write <= params->lba_end) { // almost always - single write
D2(dbg_us1=get_fpga_usec(state->fd_fparmsall[0], 0));
D6(dbg_pos = lseek(state->writer_params.blockdev_fd, 0, SEEK_CUR));
D6(fprintf(debug_file, "_w02_: starting emul_writev@ %07d\n", dbg_us1));
state->writer_params.writev_run = true;
pthread_cond_signal(&params->main_cond);
iovlen = emul_writev(state->writer_params.blockdev_fd, write_segments, num_write_segments);
state->writer_params.writev_run = false;
D2(dbg_us=get_fpga_usec(state->fd_fparmsall[0], 0); fprintf(debug_file, "_w03_: finished emul_writev, pos%%block = %ld, pos= %ld @ %07d (+ %07d, +%07d)\n",dbg_pos % PHY_BLOCK_SIZE, dbg_pos,dbg_us, (dbg_us-dbg_us1), (dbg_us-dbg_us2)));
D3(syslog (LOG_INFO, "_w03_: finished emul_writev, pos= %ld, len = %d(0x%x) @ %07d (+ %07d, +%07d)", dbg_pos, iovlen, iovlen, dbg_us, (dbg_us-dbg_us1), (dbg_us-dbg_us2)));
D2(dbg_pos = lseek64(blockdev_fd, 0, SEEK_CUR); if (dbg_pos < 0) fprintf( \
debug_file,"%slseek64()->-1, errno=%d(%s)", dbg_pref,errno, strerror(errno)));
D6(fprintf(debug_file, "%s_w02_: starting emul_writev@ %07d\n", dbg_pref, dbg_us1));
#if 0
if ((params->writev_run[1 - this_thread_index]) && // other is writing already
(get_num_empty(params) >= NUM_NEED_EMPTY)){
pthread_mutex_lock(&params->writer_mutex);
pthread_cond_signal(&params->main_cond); // signal to main thread that prepares data
pthread_mutex_unlock(&params->writer_mutex);
D3(fprintf(debug_file, "%s_w02a_: starting emul_writev and signaling main @ %07d\n", dbg_pref, get_fpga_usec(state->fd_fparmsall[0], 0)));
}
#endif
params->writev_run[this_thread_index] = true;
// pthread_cond_signal(&params->main_cond); // was always
dbg_pos64=lseek64(blockdev_fd, params->segment_pos[wpage], SEEK_SET);
if (dbg_pos64 < 0){
D0(fprintf(debug_file, "%slseek64(%d, %lld, %d)->%lld, errno=%d(%s)\n", dbg_pref,\
blockdev_fd, params->segment_pos[wpage], SEEK_SET, dbg_pos64, errno, strerror(errno)));
}
/// pthread_mutex_lock(&params->writer_mutex);
/// if (get_write_page(params)) { // ??
/// pthread_cond_broadcast(&params->writer_cond); // wake up other writer threads?
/// }
/// pthread_mutex_unlock(&params->writer_mutex);
// iovlen = emul_writev(blockdev_fd, write_segments, num_write_segments);
iovlen = 0;
for (i = 0; i < num_write_segments; i++){
params->writev_run_segm[this_thread_index] = i;
if (write_segments[i].iov_len & (PAGE_PHYS -1)){
D0(fprintf(debug_file, "Length not page-aligned: while trying direct writing of %d bytes from %p, data = 0x%llx, segment=%d\n",\
write_segments[i].iov_len, write_segments[i].iov_base, ((uint64_t*) write_segments[i].iov_base)[0], i ));
D0(syslog (LOG_INFO, "Length not page-aligned: while trying direct writing of %d bytes from %p, data = 0x%llx, segment=%d",\
write_segments[i].iov_len, write_segments[i].iov_base, ((uint64_t*) write_segments[i].iov_base)[0], i ));
debug_level = 6;
iovlen = -1;
break;
}
if (write_segments[i].iov_len > 0) {
if (i == 1) { // only for segment 1, if other write thread also is writing second (large) segment
pthread_mutex_lock(&params->writer_mutex); // maybe it will not be needed - mutex
if (params->writev_run[1 - this_thread_index] &&
(params->writev_run_segm[1-this_thread_index] >0) &&
(get_num_empty(params) >= NUM_NEED_EMPTY)){
pthread_cond_signal(&params->main_cond); // signal to main thread that prepares data
D3(dbg_us=get_fpga_usec(state->fd_fparmsall[0], 0);fprintf(debug_file, \
"%s_w02a_: starting second (large) write and signaling main <- @ %07d (+%d)\n", \
dbg_pref, dbg_us, ( dbg_us - dbg_us1)));
}
pthread_mutex_unlock(&params->writer_mutex);
}
writev_wlen = write (blockdev_fd, write_segments[i].iov_base, write_segments[i].iov_len);
// if (i == 1) { // only for segment 1, if other write thread also is writing second (large) segment
D2(fprintf(debug_file, \
"%s_w02x_: i=%d start =0x%08x, len=0x%08x\n", dbg_pref, i, (int)write_segments[i].iov_base, (int)write_segments[i].iov_len));
// }
if (writev_wlen < 0) {
D0(fprintf(debug_file, "Error %d while trying direct writing of %d bytes from %p, data = 0x%llx, segment=%d\n",\
writev_wlen, write_segments[i].iov_len, write_segments[i].iov_base, ((uint64_t*) write_segments[i].iov_base)[0],i ));
iovlen = writev_wlen;
break;
} else if (writev_wlen < write_segments[i].iov_len) {
iovlen += writev_wlen;
break;
} else {
iovlen += writev_wlen;
}
}
}
params->writev_run[this_thread_index] = false;
D2(dbg_us=get_fpga_usec(state->fd_fparmsall[0], 0); fprintf( \
debug_file, "%s_w03_: finished emul_writev, wpage=%x, thread ID=%ld, pos= %lld @ %07d (+ %07d, +%07d)\n", dbg_pref,\
wpage, pthread_self(), dbg_pos64, dbg_us, (dbg_us-dbg_us1), (dbg_us-dbg_us2)));
D4(syslog (LOG_INFO, "%s_w03_: finished emul_writev, pos= %lld, len = %d(0x%x) thread ID = %ld @ %07d (+ %07d, +%07d)", dbg_pref,\
dbg_pos, iovlen, iovlen, pthread_self(), dbg_us, (dbg_us-dbg_us1), (dbg_us-dbg_us2)));
D2(dbg_us2=dbg_us);
if (iovlen < l) {
D0(fprintf(debug_file, "emul_writev error: %s (returned %i, expected %i)\n", strerror(errno), iovlen, l));
state->writer_params.last_ret_val = -CAMOGM_FRAME_FILE_ERR;
D0(fprintf(debug_file, "%semul_writev error: %s (returned %i, expected %i)\n", dbg_pref, strerror(errno), iovlen, l));
params->last_ret_val = -CAMOGM_FRAME_FILE_ERR;
reset_rem = 1;
lseek(state->writer_params.blockdev_fd,
(state->writer_params.lba_current - state->writer_params.lba_start) * PHY_BLOCK_SIZE,
SEEK_SET); // update as if written?
} else {
// update statistic
state->rawdev.last_jpeg_size = l;
state->rawdev.total_rec_len += state->rawdev.last_jpeg_size;
D6(fprintf(debug_file, "Current position in block device: %lld\n", lseek64(state->writer_params.blockdev_fd, 0, SEEK_CUR)));
D6(fprintf(debug_file, "%sCurrent position in block device: %lld\n", dbg_pref, lseek64(blockdev_fd, 0, SEEK_CUR)));
}
} else { // Split into two writes: to the end of partition and startion from the beginning
l0 = (state->writer_params.lba_end - state->writer_params.lba_current) * PHY_BLOCK_SIZE; // remaining to be written before rollover
l0 = get_lba_next(params) * PHY_BLOCK_SIZE; // remaining to be written before rollover
l1 = l - l0; // remaining to be written after rollover
i1 = 0; // start of the second write
if (l0 > 0) { // should be always
......@@ -561,23 +937,25 @@ void *jpeg_writer(void *thread_args)
i0++; // include split chunk in the first write
}
// write the first group (before rollover)
state->writer_params.lba_current = state->writer_params.lba_start;
state->writer_params.writev_run = true;
/// params->lba_current = params->lba_start;
params->writev_run[this_thread_index] = true;
pthread_cond_signal(&params->main_cond);
iovlen = emul_writev(state->writer_params.blockdev_fd, write_segments, i0);
state->writer_params.writev_run = false;
lseek(state->writer_params.blockdev_fd, 0, SEEK_SET); // regardless of error - rewind
lseek64(blockdev_fd, params->segment_pos[wpage], SEEK_SET);
iovlen = emul_writev(blockdev_fd, write_segments, i0);
params->writev_run[this_thread_index] = false;
lseek(blockdev_fd, 0, SEEK_SET); // regardless of error - rewind
if (iovlen < l0) {
D0(fprintf(debug_file, "(1 of 2) emul_writev error: %s (returned %i, expected %i)\n", strerror(errno), iovlen, l0));
state->writer_params.last_ret_val = -CAMOGM_FRAME_FILE_ERR;
D0(fprintf(debug_file, "%s(1 of 2) emul_writev error: %s (returned %i, expected %i)\n", dbg_pref, strerror(errno), iovlen, l0));
params->last_ret_val = -CAMOGM_FRAME_FILE_ERR;
reset_rem = 1;
} else {
// do not update statistic after the first emul_writev(), do it after the second
D6(fprintf(debug_file, "(1 of 2) Current position in block device: %lld\n", lseek64(state->writer_params.blockdev_fd, 0, SEEK_CUR)));
D6(fprintf(debug_file, "%s(1 of 2) Current position in block device: %lld\n", dbg_pref, lseek64(blockdev_fd, 0, SEEK_CUR)));
// update split chunk to use its second part, shift chunks
if (l1 > 0) { // length to be written after rollover
if (write_segments[i1].iov_len != split_chunk.iov_len) { // it was split
D0(fprintf(debug_file, "chunk %d was split, full length = %d, writtren = %d\n", i1, split_chunk.iov_len, write_segments[i1].iov_len));
D0(fprintf(debug_file, "%schunk %d was split, full length = %d, writtren = %d\n", dbg_pref, i1, split_chunk.iov_len, write_segments[i1].iov_len));
write_segments[i1].iov_base += write_segments[i1].iov_len;
write_segments[i1].iov_len = split_chunk.iov_len - write_segments[i1].iov_len;
}
......@@ -586,49 +964,64 @@ void *jpeg_writer(void *thread_args)
write_segments[i0 - i1].iov_base = write_segments[i0].iov_base;
write_segments[i0 - i1].iov_len = write_segments[i0].iov_len;
}
state->writer_params.writev_run = true;
params->writev_run[this_thread_index] = true;
pthread_cond_signal(&params->main_cond);
iovlen = emul_writev(state->writer_params.blockdev_fd, write_segments, num_write_segments - i1);
state->writer_params.writev_run = false;
state->writer_params.lba_current = state->writer_params.lba_start + l1 / PHY_BLOCK_SIZE;
lseek64(blockdev_fd, 0, SEEK_SET);
iovlen = emul_writev(blockdev_fd, write_segments, num_write_segments - i1);
params->writev_run[this_thread_index] = false;
/// params->lba_current = params->lba_start + l1 / PHY_BLOCK_SIZE;
if (iovlen < l1) {
D0(fprintf(debug_file, "(2 of 2) emul_writev error: %s (returned %i, expected %i)\n", strerror(errno), iovlen, l1));
state->writer_params.last_ret_val = -CAMOGM_FRAME_FILE_ERR;
D0(fprintf(debug_file, "%s(2 of 2) emul_writev error: %s (returned %i, expected %i)\n", dbg_pref, strerror(errno), iovlen, l1));
params->last_ret_val = -CAMOGM_FRAME_FILE_ERR;
reset_rem = 1;
} else {
// update statistic (for both parts of the split write)
state->rawdev.last_jpeg_size = l;
state->rawdev.total_rec_len += state->rawdev.last_jpeg_size;
D6(fprintf(debug_file, "(1 of 2) Current position in block device: %lld\n", lseek64(state->writer_params.blockdev_fd, 0, SEEK_CUR)));
D6(fprintf(debug_file, "%s(1 of 2) Current position in block device: %lld\n", dbg_pref, lseek64(blockdev_fd, 0, SEEK_CUR)));
}
}
}
}
}
D6(fprintf(debug_file, "_w04: wrote data @ %07d\n",get_fpga_usec(state->fd_fparmsall[0], 0)));
D6(fprintf(debug_file, "_w05_: Block device positions: start = %llu, current = %llu, end = %llu\n",
state->writer_params.lba_start, state->writer_params.lba_current, state->writer_params.lba_end));
D6(fprintf(debug_file, "%s_w04: wrote data @ %07d\n", dbg_pref, get_fpga_usec(state->fd_fparmsall[0], 0)));
D6(fprintf(debug_file, "%s_w05_: Block device positions: start = %llu, current = %llu, end = %llu, params->next_segment_pos=0x%llx\n", dbg_pref,\
params->lba_start, get_lba_next(params), params->lba_end, params->next_segment_pos)); // incorrect
if ((get_lba_next(params) > params->lba_end) || (get_lba_next(params) < params->lba_start)){
D0(fprintf(debug_file, "%s_w05e_: ERROR: Block device positions: start = %llu, current = %llu, end = %llu, params->next_segment_pos=0x%llx\n", dbg_pref,\
params->lba_start, get_lba_next(params), params->lba_end, params->next_segment_pos)); // incorrect
}
} else {
D0(fprintf(debug_file, "data vector mapping error: %d)\n", num_write_segments));
state->writer_params.last_ret_val = -CAMOGM_FRAME_FILE_ERR;
D0(fprintf(debug_file, "%sdata vector mapping error: %d)\n", dbg_pref, num_write_segments));
params->last_ret_val = -CAMOGM_FRAME_FILE_ERR;
reset_rem = 1;
}
// release main thread
reset_segments(state, reset_rem, state->writer_params.chunk_page_write % SEGMENTS_PAGES);
reset_segments(state, reset_rem, wpage);
pthread_mutex_lock(&params->writer_mutex);
params->chunk_page_write = (params->chunk_page_write + 1) % (2 * SEGMENTS_PAGES);
if (params->chunk_page_prep == params->chunk_page_write) {
pthread_cond_signal(&params->main_cond); // ********** Do not signal if can write more !
D6(fprintf(debug_file, "_w06_: wrote data (%d , %d), buffer empty, signal main thread @ %07d\n", params->chunk_page_prep, params->chunk_page_write,get_fpga_usec(state->fd_fparmsall[0], 0)));
} else {
D6(fprintf(debug_file, "_w06a_: wrote data (%d , %d), has more to write, do not signal! @ %07d\n", params->chunk_page_prep, params->chunk_page_write,get_fpga_usec(state->fd_fparmsall[0], 0)));
params->chunk_page_state[wpage] = SEGPAGE_EMPTY;
// moved to while waiting
// pthread_cond_signal(&params->main_cond); // signal to main thread that prepares data
pthread_cond_broadcast(&params->writer_cond); // signal to other writer thread waiting for this one to finish writing
D6(fprintf(debug_file, "%s_w06b_: wrote data (%d , %d), signal main thread @ %07d\n", dbg_pref, \
params->chunk_page_prep, wpage, get_fpga_usec(state->fd_fparmsall[0], 0)));
}
pthread_mutex_unlock(&params->writer_mutex); // do not release untill waiting for emul_writev() or waiting for data to write
} else {
if (params->exit_write_threads) {
break;
}
}
}
params->state = STATE_STOPPED;
D5(fprintf(debug_file, "_w07_: Exit from recording thread\n")); // never?
if (close(blockdev_fd) <0) {
D0(fprintf(debug_file, "Error: %s\n", strerror(errno)));
}
// close raw file (one per thread) pthread_self(), get_fpga_usec(state->fd_fparmsall[0], 0)
D1(fprintf(debug_file, "%s_w07_: Exit from recording thread ID=%ld, @ %07d\n", dbg_pref, pthread_self(), get_fpga_usec(state->fd_fparmsall[0], 0)));
D1(syslog (LOG_INFO, "%sExit from recording thread ID=%ld, @ %07d", dbg_pref,\
pthread_self(), get_fpga_usec(state->fd_fparmsall[0], 0)));
return NULL;
}
......@@ -17,7 +17,7 @@
#ifndef _CAMOGM_JPEG_H
#define _CAMOGM_JPEG_H
#define __USE_GNU // for O_DIRECT
//#define __USE_GNU // for O_DIRECT
#include "camogm.h"
......@@ -28,6 +28,10 @@ int camogm_end_jpeg(camogm_state *state);
void camogm_free_jpeg(camogm_state *state);
int open_state_file(const rawdev_buffer *rawdev, uint64_t *current_pos);
ssize_t emul_writev (int fd, const struct iovec *iovec, int count);
int get_write_page(struct writer_params *wparams);
int get_num_empty(struct writer_params *wparams);
int get_num_busy(struct writer_params *wparams);
#endif /* _CAMOGM_JPEG_H */
......@@ -1023,6 +1023,9 @@ void *reader(void *arg)
memset(&index_sparse, 0, sizeof(struct disk_idir));
prep_socket(&sockfd, state->sock_port);
D2(syslog (LOG_INFO, "%s:line %d : Started reader thread ID=%ld @ %07d", \
__FILE__, __LINE__, pthread_self(), get_fpga_usec(state->fd_fparmsall[0], 0)));
pthread_cleanup_push(exit_thread, &exit_state);
while (true) {
fd = accept(sockfd, NULL, 0);
......@@ -1041,8 +1044,13 @@ void *reader(void *arg)
cmd_ptr = cmd_buff;
trim_command(cmd_ptr, cmd_len);
while ((cmd = parse_command(&cmd_ptr)) != -2 && state->rawdev.thread_state != STATE_CANCEL) {
if (cmd >= 0)
D6(fprintf(debug_file, "Got command '%s', number %d\n", cmd_list[cmd], cmd));
D2(syslog (LOG_INFO, "%s:line %d : Got reader command '%s', number %d @ %07d", \
__FILE__, __LINE__, cmd_list[cmd], cmd, get_fpga_usec(state->fd_fparmsall[0], 0)));
D2(fprintf(debug_file, "%s:line %d : Got reader command '%s', number %d @ %07d\n", \
__FILE__, __LINE__, cmd_list[cmd], cmd, get_fpga_usec(state->fd_fparmsall[0], 0)));
// if (cmd >= 0) {
// D6(fprintf(debug_file, "Got command '%s', number %d\n", cmd_list[cmd], cmd));
// }
switch (cmd) {
case CMD_BUILD_INDEX:
// scan raw device buffer and create disk index directory
......@@ -1050,7 +1058,7 @@ void *reader(void *arg)
delete_idir(&index_dir);
}
build_index(state, &index_dir);
D3(fprintf(debug_file, "%d files read from %s\n", index_dir.size, state->rawdev.rawdev_path));
D2(fprintf(debug_file, "%d files read from %s\n", index_dir.size, state->rawdev.rawdev_path));
break;
case CMD_GET_INDEX:
// send the content of disk index directory over socket
......@@ -1073,6 +1081,7 @@ void *reader(void *arg)
// mmap raw device buffer in MMAP_CHUNK_SIZE chunks and send them over socket
mmap_range.from = rawdev->start_pos & PAGE_BOUNDARY_MASK;
mmap_range.to = mmap_range.from + rawdev->mmap_default_size;
D2(fprintf(debug_file, "CMD_READ_DISK from = %llu, to=%llu files read from %s\n", mmap_range.from, mmap_range.to, state->rawdev.rawdev_path));
disk_chunks = (size_t)ceil((double)(rawdev->end_pos - rawdev->start_pos) / (double)rawdev->mmap_default_size);
transfer = true;
mm_file_start = rawdev->start_pos;
......@@ -1105,6 +1114,7 @@ void *reader(void *arg)
}
break;
case CMD_READ_FILE:
D2(fprintf(debug_file, "CMD_READ_FILE from %s\n", state->rawdev.rawdev_path));
// read single file by offset given
if (index_dir.size > 0) {
struct disk_index indx;
......@@ -1116,6 +1126,7 @@ void *reader(void *arg)
break;
case CMD_FIND_FILE: {
// find file by time stamp
D2(fprintf(debug_file, "CMD_FIND_FILE from %s\n", state->rawdev.rawdev_path));
struct disk_index indx;
struct disk_index *indx_ptr = NULL;
if (get_timestamp_args(cmd_ptr, &indx) > 0) {
......@@ -1133,6 +1144,7 @@ void *reader(void *arg)
}
case CMD_NEXT_FILE: {
// read next file after previously found file
D2(fprintf(debug_file, "CMD_NEXT_FILE from %s\n", state->rawdev.rawdev_path));
struct range rng;
struct disk_index *new_indx = NULL;
struct disk_index *indx_ptr = NULL;
......@@ -1166,11 +1178,13 @@ void *reader(void *arg)
break;
}
case CMD_PREV_FILE: {
D2(fprintf(debug_file, "CMD_PREV_FILE from %s\n", state->rawdev.rawdev_path));
break;
}
case CMD_READ_ALL_FILES:
// read files from raw device buffer and send them over socket; the disk index directory
// should be built beforehand
D2(fprintf(debug_file, "CMD_READ_ALL_FILES from %s\n", state->rawdev.rawdev_path));
if (index_dir.size > 0) {
send_fnum(fd, index_dir.size);
close(fd);
......@@ -1235,7 +1249,10 @@ void *reader(void *arg)
usleep(COMMAND_LOOP_DELAY);
}
pthread_cleanup_pop(0);
D2(fprintf(debug_file, "%s:line %d : Exited reader thread ID=%ld @ %07d\n", \
__FILE__, __LINE__, pthread_self(), get_fpga_usec(state->fd_fparmsall[0], 0)));
D2(syslog (LOG_INFO, "%s:line %d : Exited reader thread ID=%ld @ %07d", \
__FILE__, __LINE__, pthread_self(), get_fpga_usec(state->fd_fparmsall[0], 0)));
return (void *) 0;
}
......@@ -1296,7 +1313,7 @@ static void build_index(camogm_state *state, struct disk_idir *idir)
uint64_t include_st_marker, include_en_marker;
size_t add_stm_len, add_enm_len;
struct disk_index *node = NULL;
D2(fprintf(debug_file, "%s:line %d: state->rawdev.rawdev_path=%s\n", __FILE__, __LINE__, state->rawdev.rawdev_path));
state->rawdev.rawdev_fd = open(state->rawdev.rawdev_path, O_RDONLY);
if (state->rawdev.rawdev_fd < 0) {
D0(perror(__func__));
......
......@@ -69,7 +69,7 @@
*/
define('SSD_ROOT', '/mnt/sda1/');
define('ASSUME_EXT4', 'sda1'); // comment out after testing
define('USE_SYSLOG', 1);
//define('USE_SYSLOG', 1);
$cmd = $_GET['cmd'];
$debug = $_GET['debug'];
$debuglev = $_GET['debuglev'];
......@@ -81,8 +81,9 @@ $default_state = "/etc/elphel393/camogm.disk";
//$state_file = "/mnt/sda1/camogm.disk";
$state_file = "/etc/elphel393/camogm.disk";
$start_str = "camogm -n " . $cmd_pipe . " -p " . $cmd_port;
if (defined('USE_SYSLOG')) {
syslog(LOG_NOTICE, "camogm_interface.php:");
//if (defined('USE_SYSLOG')) {
if (USE_SYSLOG > 0) {
syslog(LOG_NOTICE, "camogm_interface.php:");
foreach ($_GET as $k => $v){
syslog(LOG_NOTICE, $k ." -> ".$v);
}
......@@ -138,9 +139,9 @@ if ($cmd == "run_camogm")
}
// set fast recording mode if there is at least one suitable partition or revert to legacy 'mov' mode
if (defined('USE_SYSLOG')) syslog(LOG_NOTICE, "before get_raw_dev()");
if (USE_SYSLOG > 0) syslog(LOG_NOTICE, "before get_raw_dev()");
$partitions = get_raw_dev();
if (defined('USE_SYSLOG')) syslog(LOG_NOTICE, "after get_raw_dev()");
if (USE_SYSLOG > 0) syslog(LOG_NOTICE, "after get_raw_dev()");
if (!empty($partitions)) {
reset($partitions);
$cmd_str = 'format=jpeg;' . 'rawdev_path=' . key($partitions) . ';';
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment