/**
* @file o3000_xfer_handler.c
* @brief O-3000 USB transfer handler
* @author Patrick Brunner - brunner@stettbacher.ch
* @author Patrick Roth - roth@stettbacher.ch
* @version 1.1
* @date 2016-03-01
* @copyright 2012-2016 Stettbacher Signal Processing AG
*
* @remarks
*
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
*
*/
#include
#include "o3000.h"
#include "o3000_private.h"
/**
* The preamble, used to detect the beginning of a new frame
*/
static const uint8_t preamble[] = {0xaa, 0x55, 0xde, 0xad, 0xbe, 0xef, 0x55, 0xaa};
/**
* Check whether the argument points to a valid preamble.
*
* @param p Memory pointer
* @return TRUE: points to valid preamble, FALSE: no valid preamble
*/
static int is_preamble(uint8_t *p) {
if( (*p == preamble[7]) &&
(*(p+1) == preamble[6]) &&
(*(p+2) == preamble[5]) &&
(*(p+3) == preamble[4]) &&
(*(p+4) == preamble[3]) &&
(*(p+5) == preamble[2]) &&
(*(p+6) == preamble[1]) &&
(*(p+7) == preamble[0])) {
return TRUE;
}
else {
return FALSE;
}
}
/**
* Scan for the preamble in the buffer specified.
*
* @param buf Buffer start address
* @param size Buffer size
* @return preamble offset in bytes (w.r.t. to start address) or -1 if not found
*/
static int scan_for_preamble(uint8_t *buf, int size) {
int i, size_preamble, end;
size_preamble = sizeof(((struct img_header_t*)0)->preamble);
end = size - size_preamble;
for(i = 0; i < end; i++) {
if(is_preamble(buf+i) == TRUE) {
return i; // preamble found at offset i
}
}
return -1; // preamble not found
}
/**
* Video transfer handler
*
* This function recursively scans through a video transfer (video data chunk).
* Incomplete image frames are tracked continuously. Whenever an image frame is completed, the video callback handler is called.
*
* NOTE
* The video-cache consists of severalf memory-adjacent USB transfers plus an adjacent framebuffer. In case of a wrap-around
* the end of the image frame (located at the start of the video-cache) is copied to the framebuffer. So this frame won't be fragmented.
* In case of receiving short USB transfers, two image frames won't be adjacent at the video-cache. Therfore, after handling a image
* the state machine is set to not-synced state.
*
* @param session current session (is never NULL otherwise SEGFAULT)
* @param addr Start address of block to be scanned.
* @param len Lenght of block.
*/
void handle_transfer(struct o3000_session_t *session, uint8_t *addr, int len) {
int offset;
struct img_header_t *img_header; // points to current image header structure
int wraparound_chunk_size; // chunk size (bytes) at wrap-around to copy to frame buffer
int frame_data_remain; // remaining image frame data to process in bytes
if(session->frame_state == IMG_FRAME_STATE_NOSYNC) {
offset = scan_for_preamble(addr, len);
if(offset < 0) {
o3000_log(session, O3000_LOG_WARNING, "%s: preamble not found (start address=%p, len=%d)\n",
__func__, addr - session->video_cache, len);
return;
}
// preamble found, we are synchronized now!
session->frame_state = IMG_FRAME_STATE_SYNC;
session->frame_start = addr + offset; // set start address of image frame
session->frame_rx_cnt = 0; // reset data counter
len -= offset; // skip image data located before preamble
o3000_log(session, O3000_LOG_DEBUG, "%s: preamble found at address offset %d, remaining data of current image frame %d bytes\n",
__func__, session->frame_start - session->video_cache, len);
// parse image header
img_header = (struct img_header_t*)(session->frame_start);
session->frame_img_header = img_header;
session->image_start = session->frame_start + IMAGE_HEADER_SIZE + img_header->image_start;
session->frame_size = img_header->payload_size + IMAGE_HEADER_SIZE;
}
if(session->frame_state == IMG_FRAME_STATE_SYNC) {
session->frame_rx_cnt += len;
o3000_log(session, O3000_LOG_DEBUG, "%s: sync: %d bytes of %d bytes (image size) received\n",
__func__, session->frame_rx_cnt, session->frame_size);
// checking whether whole image frame is received
if(session->frame_rx_cnt >= session->frame_size) {
o3000_log(session, O3000_LOG_DEBUG, "%s: sync: image frame received\n", __func__);
if(session->frame_start > addr) {
/*
* We have a wrap-around to resolve. Do copy part of the image frame which resides at the
* start of the video cache to the frame buffer. Note that the frame buffer is memory-adjacent
* to the video cache.
*/
wraparound_chunk_size = session->frame_size - (session->frame_buf - session->frame_start);
if(wraparound_chunk_size < 0 || wraparound_chunk_size > MAX_IMAGE_SIZE) {
/*
* Paranoia check:
* Ensure that the wraparound_chunk_size is always positive and does not exceed the end of the frame_buf.
* (Cases were noticed where the wraparound_chunk_size becomes negative.)
*/
session->frame_state = IMG_FRAME_STATE_NOSYNC;
return;
}
o3000_log(session, O3000_LOG_DEBUG, "%s: wrap-around, copy %d bytes to frame buffer\n", __func__, wraparound_chunk_size);
memcpy(session->frame_buf, session->video_cache, wraparound_chunk_size);
}
// delegate image to overlaying application
session->video_cb(session->id, session->image_start, session->frame_img_header);
/*
* Image frame is processed.
* The following image may won't be memory-adjacent to this one. Because a snapshot
* is sent with zero packet. The easiest way is to change state to NOT SYNC to trigger
* syncronization part again.
*/
session->frame_state = IMG_FRAME_STATE_NOSYNC;
frame_data_remain = session->frame_rx_cnt - session->frame_size;
o3000_log(session, O3000_LOG_DEBUG, "%s: sync: %d bytes remaining from next image frame\n", __func__, frame_data_remain);
if(frame_data_remain > 0) {
handle_transfer(session, addr+(len-frame_data_remain), frame_data_remain);
}
}
}
}