aboutsummaryrefslogtreecommitdiffstats
path: root/o3000_xfer_handler.c
blob: d0cc73bad25ef5b2cd1bd82a685887a1757c3a7f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
/**
* @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
*
* <PRE>
* 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
* </PRE>
*
*/


#include <string.h>

#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);
			}
		}
	}
}