root/trunk/libffado/src/libstreaming/amdtp-oxford/AmdtpOxfordReceiveStreamProcessor.cpp

Revision 1535, 10.1 kB (checked in by ppalmers, 14 years ago)

add support for the FCA-202, and possibly other Oxford FW-92x devices

Line 
1 /*
2  * Copyright (C) 2005-2008 by Pieter Palmers
3  *
4  * This file is part of FFADO
5  * FFADO = Free Firewire (pro-)audio drivers for linux
6  *
7  * FFADO is based upon FreeBoB.
8  *
9  * This program is free software: you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation, either version 2 of the License, or
12  * (at your option) version 3 of the License.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
21  *
22  */
23
24
25 #include "AmdtpOxfordReceiveStreamProcessor.h"
26 #include "../StreamProcessorManager.h"
27 #include "devicemanager.h"
28
29 #include "libieee1394/cycletimer.h"
30
31 #define DLL_PI        (3.141592653589793238)
32 #define DLL_SQRT2     (1.414213562373095049)
33 #define DLL_2PI       (2.0 * DLL_PI)
34
35 // DLL bandwidth in Hz
36 #define DLL_BANDWIDTH_HZ  1.0
37
38 namespace Streaming {
39
40 /* --------------------- RECEIVE ----------------------- */
41
42 /* The issues with the FCA202 (and possibly other oxford FW92x devices) are:
43  * - they transmit in non-blocking mode
44  * - the timestamps are not correct
45  *
46  * This requires some workarounds.
47  *
48  * The approach is to 'convert' non-blocking into blocking so
49  * that we can use the existing streaming system.
50  *
51  * The streamprocessor has a "pseudo-packet" buffer that will collect
52  * all frames present. Once one SYT_INTERVAL of frames is present, we indicate
53  * that a packet has been received.
54  *
55  * To overcome the timestamping issue we use the time-of-arrival as a timestamp.
56  *
57  */
58
59 AmdtpOxfordReceiveStreamProcessor::AmdtpOxfordReceiveStreamProcessor(FFADODevice &parent, int dimension)
60     : AmdtpReceiveStreamProcessor(parent, dimension)
61     , m_next_packet_timestamp ( 0xFFFFFFFF )
62     , m_temp_buffer( NULL )
63     , m_packet_size_bytes( 0 )
64     , m_payload_buffer( NULL )
65     , m_dll_e2 ( 0 )
66     , m_dll_b ( 0 )
67     , m_dll_c ( 0 )
68     , m_expected_time_of_receive ( 0xFFFFFFFF )
69     , m_nominal_ticks_per_frame( 0 )
70 {}
71
72 AmdtpOxfordReceiveStreamProcessor::~AmdtpOxfordReceiveStreamProcessor()
73 {
74     if(m_temp_buffer) ffado_ringbuffer_free(m_temp_buffer);
75     if(m_payload_buffer) free(m_payload_buffer);
76 }
77
78 bool
79 AmdtpOxfordReceiveStreamProcessor::prepareChild()
80 {
81     debugOutput( DEBUG_LEVEL_VERBOSE, "Preparing (%p)...\n", this);
82
83     int packet_payload_size_events = m_dimension * getSytInterval();
84
85     // allocate space for four packets payload. The issue is that it can be
86     // that we receive a bit too much payload such that the total is more
87     // than one packet
88     FFADO_ASSERT( m_temp_buffer == NULL );
89     if( !(m_temp_buffer = ffado_ringbuffer_create(
90             packet_payload_size_events * 4 * 4))) {
91         debugFatal("Could not allocate memory event ringbuffer\n");
92         return false;
93     }
94
95     m_next_packet_timestamp = 0xFFFFFFFF;
96
97     m_packet_size_bytes = getSytInterval() * m_dimension * sizeof(quadlet_t);
98     m_payload_buffer = (char *)malloc(m_packet_size_bytes);
99
100     if(m_payload_buffer == NULL) {
101         debugFatal("could not allocate memory for payload buffer\n");
102         return false;
103     }
104
105     // init the DLL
106     unsigned int nominal_frames_per_second = m_StreamProcessorManager.getNominalRate();
107     m_nominal_ticks_per_frame = (double)TICKS_PER_SECOND / (double)nominal_frames_per_second;
108     m_dll_e2 = m_nominal_ticks_per_frame * (double)getSytInterval();
109
110     double tupdate = m_nominal_ticks_per_frame * (double)getSytInterval();
111     double bw_rel = DLL_BANDWIDTH_HZ / (double)TICKS_PER_SECOND * tupdate;
112     if(bw_rel >= 0.5) {
113         debugError("Requested bandwidth out of range: %f > %f\n", DLL_BANDWIDTH_HZ / (double)TICKS_PER_SECOND, 0.5 / tupdate);
114         return false;
115     }
116     m_dll_b = bw_rel * (DLL_SQRT2 * DLL_2PI);
117     m_dll_c = bw_rel * bw_rel * DLL_2PI * DLL_2PI;
118
119     return AmdtpReceiveStreamProcessor::prepareChild();
120 }
121
122 /**
123  * Processes packet header to extract timestamps and so on
124  *
125  * this will be abused to copy the payload into the temporary buffer
126  * once the buffer is full, we trigger a data-read operation
127  *
128  * @param data
129  * @param length
130  * @param channel
131  * @param tag
132  * @param sy
133  * @param pkt_ctr CTR value when packet was received
134  * @return
135  */
136 enum StreamProcessor::eChildReturnValue
137 AmdtpOxfordReceiveStreamProcessor::processPacketHeader(unsigned char *data, unsigned int length,
138                                                        unsigned char tag, unsigned char sy,
139                                                        uint32_t pkt_ctr)
140 {
141     struct iec61883_packet *packet = (struct iec61883_packet *) data;
142     FFADO_ASSERT(packet);
143     debugOutput(DEBUG_LEVEL_VERY_VERBOSE, "Packet at %03lu %04lu %04lu\n",
144                                         CYCLE_TIMER_GET_SECS(pkt_ctr), CYCLE_TIMER_GET_CYCLES(pkt_ctr), CYCLE_TIMER_GET_OFFSET(pkt_ctr));
145
146     bool ok = (packet->fdf != 0xFF) &&
147               (packet->fmt == 0x10) &&
148               (packet->dbs > 0) &&
149               (length >= 2*sizeof(quadlet_t));
150     if(ok) { // there is payload
151         debugOutput(DEBUG_LEVEL_VERY_VERBOSE, "Packet with payload\n");
152         unsigned int frames_in_tempbuffer = ffado_ringbuffer_read_space(m_temp_buffer) / sizeof (quadlet_t) / m_dimension;
153
154         // if the next packet tsp isn't present, generate one now
155         if (m_next_packet_timestamp == 0xFFFFFFFF) {
156             uint64_t toa = CYCLE_TIMER_TO_TICKS(pkt_ctr);
157
158             // add some extra time, to ensure causality
159             // in a normal system, the SYT's are max 2 cycles apart
160             toa = addTicks(toa, 2*TICKS_PER_CYCLE);
161
162             // correct for the frames already in the buffer
163             toa = substractTicks(toa, (uint64_t)(m_nominal_ticks_per_frame * frames_in_tempbuffer));
164            
165             // toa now contains the CTR in ticks of the first frame in the buffer
166             // if we calculate it from the time-of-arrival of the current packet
167            
168             // init if required
169             if (m_expected_time_of_receive >= 0xFFFFFFFE) {
170                 m_expected_time_of_receive = substractTicks(toa, (uint64_t)m_dll_e2);
171             }
172
173             // time-of-arrival as timestamp is very jittery, filter this a bit
174             double err = diffTicks(toa, m_expected_time_of_receive);
175            
176             // if err is too large, reinitialize as this is most likely a discontinuity in
177             // the streams (e.g. packet lost, xrun)
178             if (err > m_dll_e2 * 2.0 || err < -m_dll_e2 * 2.0 ) {
179                 err = 0.0;
180                 m_expected_time_of_receive = toa;
181             }
182
183             m_next_packet_timestamp = m_expected_time_of_receive;
184             double corr = (m_dll_b * err + m_dll_e2);
185             if (corr > 0) {
186                 m_expected_time_of_receive = addTicks(m_expected_time_of_receive, (uint64_t)corr);
187             } else {
188                 m_expected_time_of_receive = substractTicks(m_expected_time_of_receive, (uint64_t)(-corr));
189             }
190             m_dll_e2 += m_dll_c * err;
191
192             debugOutput(DEBUG_LEVEL_VERY_VERBOSE, "Generated TSP: %16llu %lld %d %d\n",
193                                              m_next_packet_timestamp, m_next_packet_timestamp-m_last_timestamp,
194                                              frames_in_tempbuffer,  ((length / sizeof (quadlet_t)) - 2) / m_dimension);
195         }
196
197         // add the payload to the temporary ringbuffer
198         quadlet_t *payload_start = (quadlet_t *)(data + 8);
199         FFADO_ASSERT(m_dimension == packet->dbs);
200         unsigned int nevents = ((length / sizeof (quadlet_t)) - 2) / m_dimension;
201         unsigned int write_size = nevents * sizeof(quadlet_t) * m_dimension;
202         debugOutput(DEBUG_LEVEL_VERY_VERBOSE, "Payload: %u events, going to write %u bytes\n", nevents, write_size);
203         unsigned int written = 0;
204         if ((written = ffado_ringbuffer_write(m_temp_buffer, (char *)payload_start, write_size)) < write_size)
205         {
206             debugFatal("Temporary ringbuffer full (wrote %u bytes of %u)\n", written, write_size);
207 //             return eCRV_Error;
208         }
209
210         // now figure out if we have sufficient frames in the tempbuffer to construct a packet
211         unsigned int quadlets_in_tempbuffer = frames_in_tempbuffer * sizeof (quadlet_t);
212         if (quadlets_in_tempbuffer >= m_syt_interval * m_dimension) { // yes
213             debugOutput(DEBUG_LEVEL_VERY_VERBOSE, "Sufficient frames in buffer: %u (need %u)\n", quadlets_in_tempbuffer/m_dimension, m_syt_interval);
214             m_last_timestamp = m_next_packet_timestamp;
215             m_next_packet_timestamp = 0xFFFFFFFF; // next cycle should generate a new timestamp
216
217             // read the 'packet' into the packet payload buffer
218             ffado_ringbuffer_read(m_temp_buffer, m_payload_buffer, m_packet_size_bytes);
219
220             // signal a packet reception
221             return eCRV_OK;
222         } else { // no
223             debugOutput(DEBUG_LEVEL_VERY_VERBOSE, "Insufficient frames in buffer: %u (need %u)\n", quadlets_in_tempbuffer/m_dimension, m_syt_interval);
224             return eCRV_Invalid;
225         }
226
227     } else {
228         return eCRV_Invalid;
229     }
230 }
231
232 /**
233  * extract the data from the packet
234  *
235  * instead of using the data from the packet, we use the data from our 'internal' packet
236  *
237  * @pre the IEC61883 packet is valid according to isValidPacket
238  * @param data
239  * @param length
240  * @param channel
241  * @param tag
242  * @param sy
243  * @param pkt_ctr
244  * @return
245  */
246 enum StreamProcessor::eChildReturnValue
247 AmdtpOxfordReceiveStreamProcessor::processPacketData(unsigned char *data, unsigned int length) {
248     struct iec61883_packet *packet = (struct iec61883_packet *) data;
249     assert(packet);
250
251     debugOutput(DEBUG_LEVEL_VERY_VERBOSE, "Processing data\n");
252
253     // put whatever is in the payload buffer at the moment into the timestamped buffer
254     if(m_data_buffer->writeFrames(m_syt_interval, m_payload_buffer, m_last_timestamp)) {
255         return eCRV_OK;
256     } else {
257         return eCRV_XRun;
258     }
259 }
260
261 } // end of namespace Streaming
Note: See TracBrowser for help on using the browser.