/** * @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); } } } }