xref: /btstack/example/sco_demo_util.c (revision 25234a7e05f3e69524e0ba3eec834a229a86d91b)
1 /*
2  * Copyright (C) 2016 BlueKitchen GmbH
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  *
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. Neither the name of the copyright holders nor the names of
14  *    contributors may be used to endorse or promote products derived
15  *    from this software without specific prior written permission.
16  * 4. Any redistribution, use, or modification is done solely for
17  *    personal benefit and not for any commercial purpose or for
18  *    monetary gain.
19  *
20  * THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS
21  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
23  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS
24  * RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
25  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
26  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
27  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
28  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
29  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
30  * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  *
33  * Please inquire about commercial licensing options at
34  * [email protected]
35  *
36  */
37 
38 /*
39  * sco_demo_util.c - send/receive test data via SCO, used by hfp_*_demo and hsp_*_demo
40  */
41 
42 
43 #include <stdio.h>
44 
45 #include "sco_demo_util.h"
46 #include "btstack_debug.h"
47 #include "classic/btstack_sbc.h"
48 #include "classic/btstack_cvsd_plc.h"
49 #include "classic/hfp_msbc.h"
50 #include "classic/hfp.h"
51 
52 #ifdef HAVE_POSIX_FILE_IO
53 #include "wav_util.h"
54 #endif
55 
56 // configure test mode
57 #define SCO_DEMO_MODE_SINE		0
58 #define SCO_DEMO_MODE_ASCII		1
59 #define SCO_DEMO_MODE_COUNTER	2
60 
61 
62 // SCO demo configuration
63 #define SCO_DEMO_MODE SCO_DEMO_MODE_SINE
64 #define SCO_REPORT_PERIOD 100
65 
66 #ifdef HAVE_POSIX_FILE_IO
67 #define SCO_WAV_FILENAME      "sco_input.wav"
68 #define SCO_MSBC_OUT_FILENAME "sco_output.msbc"
69 #define SCO_MSBC_IN_FILENAME  "sco_input.mscb"
70 
71 #define SCO_WAV_DURATION_IN_SECONDS 15
72 #endif
73 
74 
75 #if defined(HAVE_PORTAUDIO) && (SCO_DEMO_MODE == SCO_DEMO_MODE_SINE)
76 #define USE_PORTAUDIO
77 #endif
78 
79 #ifdef USE_PORTAUDIO
80 #include <portaudio.h>
81 #include "btstack_ring_buffer.h"
82 
83 // portaudio config
84 #define NUM_CHANNELS            1
85 
86 #define CVSD_SAMPLE_RATE        8000
87 #define CVSD_FRAMES_PER_BUFFER  24
88 #define CVSD_PA_SAMPLE_TYPE     paInt8
89 #define CVSD_BYTES_PER_FRAME    (1*NUM_CHANNELS)
90 #define CVSD_PREBUFFER_MS       5
91 #define CVSD_PREBUFFER_BYTES    (CVSD_PREBUFFER_MS * CVSD_SAMPLE_RATE/1000 * CVSD_BYTES_PER_FRAME)
92 
93 #define MSBC_SAMPLE_RATE        16000
94 #define MSBC_FRAMES_PER_BUFFER  120
95 #define MSBC_PA_SAMPLE_TYPE     paInt16
96 #define MSBC_BYTES_PER_FRAME    (2*NUM_CHANNELS)
97 #define MSBC_PREBUFFER_MS       50
98 #define MSBC_PREBUFFER_BYTES    (MSBC_PREBUFFER_MS * MSBC_SAMPLE_RATE/1000 * MSBC_BYTES_PER_FRAME)
99 
100 // portaudio globals
101 static  PaStream * stream;
102 static uint8_t pa_stream_started = 0;
103 
104 static uint8_t ring_buffer_storage[2*MSBC_PREBUFFER_BYTES];
105 static btstack_ring_buffer_t ring_buffer;
106 #endif
107 
108 
109 static int dump_data = 1;
110 static int count_sent = 0;
111 static int count_received = 0;
112 static uint8_t negotiated_codec = 0;
113 static int num_audio_frames = 0;
114 
115 FILE * msbc_file_in;
116 FILE * msbc_file_out;
117 
118 #if SCO_DEMO_MODE == SCO_DEMO_MODE_SINE
119 
120 // input signal: pre-computed sine wave, at 8000 kz
121 static const uint8_t sine_uint8[] = {
122       0,  15,  31,  46,  61,  74,  86,  97, 107, 114,
123     120, 124, 126, 126, 124, 120, 114, 107,  97,  86,
124      74,  61,  46,  31,  15,   0, 241, 225, 210, 195,
125     182, 170, 159, 149, 142, 136, 132, 130, 130, 132,
126     136, 142, 149, 159, 170, 182, 195, 210, 225, 241,
127 };
128 
129 
130 // input signal: pre-computed sine wave, 160 Hz at 16000 kHz
131 static const int16_t sine_int16[] = {
132      0,    2057,    4107,    6140,    8149,   10126,   12062,   13952,   15786,   17557,
133  19260,   20886,   22431,   23886,   25247,   26509,   27666,   28714,   29648,   30466,
134  31163,   31738,   32187,   32509,   32702,   32767,   32702,   32509,   32187,   31738,
135  31163,   30466,   29648,   28714,   27666,   26509,   25247,   23886,   22431,   20886,
136  19260,   17557,   15786,   13952,   12062,   10126,    8149,    6140,    4107,    2057,
137      0,   -2057,   -4107,   -6140,   -8149,  -10126,  -12062,  -13952,  -15786,  -17557,
138 -19260,  -20886,  -22431,  -23886,  -25247,  -26509,  -27666,  -28714,  -29648,  -30466,
139 -31163,  -31738,  -32187,  -32509,  -32702,  -32767,  -32702,  -32509,  -32187,  -31738,
140 -31163,  -30466,  -29648,  -28714,  -27666,  -26509,  -25247,  -23886,  -22431,  -20886,
141 -19260,  -17557,  -15786,  -13952,  -12062,  -10126,   -8149,   -6140,   -4107,   -2057,
142 };
143 
144 static int phase = 0;
145 static void sco_demo_sine_wave_int8(int num_samples, int8_t * data){
146     int i;
147     for (i=0; i<num_samples; i++){
148         data[i] = (int8_t)sine_uint8[phase];
149         phase++;
150         if (phase >= sizeof(sine_uint8)) phase = 0;
151     }
152 }
153 
154 static void sco_demo_sine_wave_int16(int num_samples, int16_t * data){
155     int i;
156     for (i=0; i < num_samples; i++){
157         data[i] = sine_int16[phase++];
158         if (phase >= (sizeof(sine_int16) / sizeof(int16_t))){
159             phase = 0;
160         }
161     }
162 }
163 
164 static void sco_demo_fill_audio_frame(void){
165     if (!hfp_msbc_can_encode_audio_frame_now()) return;
166     int num_samples = hfp_msbc_num_audio_samples_per_frame();
167     int16_t sample_buffer[num_samples];
168     sco_demo_sine_wave_int16(num_samples, sample_buffer);
169     hfp_msbc_encode_audio_frame(sample_buffer);
170     num_audio_frames++;
171 }
172 
173 #ifdef SCO_WAV_FILENAME
174 static btstack_sbc_decoder_state_t decoder_state;
175 static btstack_cvsd_plc_state_t cvsd_plc_state;
176 static int num_samples_to_write;
177 
178 #ifdef USE_PORTAUDIO
179 static int patestCallback( const void *inputBuffer, void *outputBuffer,
180                            unsigned long framesPerBuffer,
181                            const PaStreamCallbackTimeInfo* timeInfo,
182                            PaStreamCallbackFlags statusFlags,
183                            void *userData ) {
184     (void) timeInfo; /* Prevent unused variable warnings. */
185     (void) statusFlags;
186     (void) inputBuffer;
187 
188     uint32_t bytes_read = 0;
189     int bytes_per_buffer = framesPerBuffer;
190     if (negotiated_codec == HFP_CODEC_MSBC){
191         bytes_per_buffer *= MSBC_BYTES_PER_FRAME;
192     } else {
193         bytes_per_buffer *= CVSD_BYTES_PER_FRAME;
194     }
195 
196     if (btstack_ring_buffer_bytes_available(&ring_buffer) >= bytes_per_buffer){
197         btstack_ring_buffer_read(&ring_buffer, outputBuffer, bytes_per_buffer, &bytes_read);
198     } else {
199         printf("NOT ENOUGH DATA!\n");
200         memset(outputBuffer, 0, bytes_per_buffer);
201     }
202     // printf("bytes avail after read: %d\n", btstack_ring_buffer_bytes_available(&ring_buffer));
203     return 0;
204 }
205 #endif
206 
207 static void handle_pcm_data(int16_t * data, int num_samples, int num_channels, int sample_rate, void * context){
208     // printf("handle_pcm_data num samples %u, sample rate %d\n", num_samples, num_channels);
209 #ifdef USE_PORTAUDIO
210     if (!pa_stream_started && btstack_ring_buffer_bytes_available(&ring_buffer) >= MSBC_PREBUFFER_BYTES){
211         /* -- start stream -- */
212         PaError err = Pa_StartStream(stream);
213         if (err != paNoError){
214             printf("Error starting the stream: \"%s\"\n",  Pa_GetErrorText(err));
215             return;
216         }
217         pa_stream_started = 1;
218     }
219     btstack_ring_buffer_write(&ring_buffer, (uint8_t *)data, num_samples*num_channels*2);
220     // printf("bytes avail after write: %d\n", btstack_ring_buffer_bytes_available(&ring_buffer));
221 #endif
222 
223     if (!num_samples_to_write) return;
224 
225     num_samples = btstack_min(num_samples, num_samples_to_write);
226     num_samples_to_write -= num_samples;
227 
228     wav_writer_write_int16(num_samples, data);
229 
230     if (num_samples_to_write == 0){
231         sco_demo_close();
232     }
233 }
234 
235 static void sco_demo_init_mSBC(void){
236     int sample_rate = 16000;
237     wav_writer_open(SCO_WAV_FILENAME, 1, sample_rate);
238     btstack_sbc_decoder_init(&decoder_state, SBC_MODE_mSBC, &handle_pcm_data, NULL);
239 
240     num_samples_to_write = sample_rate * SCO_WAV_DURATION_IN_SECONDS;
241 
242     hfp_msbc_init();
243     sco_demo_fill_audio_frame();
244 
245 #ifdef SCO_MSBC_IN_FILENAME
246     msbc_file_in = fopen(SCO_MSBC_IN_FILENAME, "wb");
247     printf("SCO Demo: creating mSBC in file %s, %p\n", SCO_MSBC_IN_FILENAME, msbc_file_in);
248 #endif
249 #ifdef SCO_MSBC_OUT_FILENAME
250     msbc_file_out = fopen(SCO_MSBC_OUT_FILENAME, "wb");
251     printf("SCO Demo: creating mSBC out file %s, %p\n", SCO_MSBC_OUT_FILENAME, msbc_file_out);
252 #endif
253 
254 #ifdef USE_PORTAUDIO
255     PaError err;
256     PaStreamParameters outputParameters;
257 
258     /* -- initialize PortAudio -- */
259     err = Pa_Initialize();
260     if( err != paNoError ) return;
261     /* -- setup input and output -- */
262     outputParameters.device = Pa_GetDefaultOutputDevice(); /* default output device */
263     outputParameters.channelCount = NUM_CHANNELS;
264     outputParameters.sampleFormat = MSBC_PA_SAMPLE_TYPE;
265     outputParameters.suggestedLatency = Pa_GetDeviceInfo( outputParameters.device )->defaultHighOutputLatency;
266     outputParameters.hostApiSpecificStreamInfo = NULL;
267     /* -- setup stream -- */
268     err = Pa_OpenStream(
269            &stream,
270            NULL, // &inputParameters,
271            &outputParameters,
272            MSBC_SAMPLE_RATE,
273            MSBC_FRAMES_PER_BUFFER,
274            paClipOff, /* we won't output out of range samples so don't bother clipping them */
275            patestCallback,      /* no callback, use blocking API */
276            NULL );    /* no callback, so no callback userData */
277     if (err != paNoError){
278         printf("Error initializing portaudio: \"%s\"\n",  Pa_GetErrorText(err));
279         return;
280     }
281     memset(ring_buffer_storage, 0, sizeof(ring_buffer_storage));
282     btstack_ring_buffer_init(&ring_buffer, ring_buffer_storage, sizeof(ring_buffer_storage));
283     pa_stream_started = 0;
284 #endif
285 }
286 
287 static void sco_demo_receive_mSBC(uint8_t * packet, uint16_t size){
288     if (num_samples_to_write){
289         if (msbc_file_in){
290             // log incoming mSBC data for testing
291             fwrite(packet+3, size-3, 1, msbc_file_in);
292         }
293     }
294     btstack_sbc_decoder_process_data(&decoder_state, (packet[1] >> 4) & 3, packet+3, size-3);
295 }
296 
297 static void sco_demo_init_CVSD(void){
298     int sample_rate = 8000;
299     wav_writer_open(SCO_WAV_FILENAME, 1, sample_rate);
300     btstack_cvsd_plc_init(&cvsd_plc_state);
301     num_samples_to_write = sample_rate * SCO_WAV_DURATION_IN_SECONDS;
302 
303 #ifdef USE_PORTAUDIO
304     PaError err;
305     PaStreamParameters outputParameters;
306 
307     /* -- initialize PortAudio -- */
308     err = Pa_Initialize();
309     if( err != paNoError ) return;
310     /* -- setup input and output -- */
311     outputParameters.device = Pa_GetDefaultOutputDevice(); /* default output device */
312     outputParameters.channelCount = NUM_CHANNELS;
313     outputParameters.sampleFormat = CVSD_PA_SAMPLE_TYPE;
314     outputParameters.suggestedLatency = Pa_GetDeviceInfo( outputParameters.device )->defaultHighOutputLatency;
315     outputParameters.hostApiSpecificStreamInfo = NULL;
316     /* -- setup stream -- */
317     err = Pa_OpenStream(
318            &stream,
319            NULL, // &inputParameters,
320            &outputParameters,
321            CVSD_SAMPLE_RATE,
322            CVSD_FRAMES_PER_BUFFER,
323            paClipOff, /* we won't output out of range samples so don't bother clipping them */
324            patestCallback,      /* no callback, use blocking API */
325            NULL );    /* no callback, so no callback userData */
326     if (err != paNoError){
327         printf("Error initializing portaudio: \"%s\"\n",  Pa_GetErrorText(err));
328         return;
329     }
330     memset(ring_buffer_storage, 0, sizeof(ring_buffer_storage));
331     btstack_ring_buffer_init(&ring_buffer, ring_buffer_storage, sizeof(ring_buffer_storage));
332     pa_stream_started = 0;
333 #endif
334 }
335 
336 static void sco_demo_receive_CVSD(uint8_t * packet, uint16_t size){
337     if (!num_samples_to_write) return;
338 
339     const int num_samples = size - 3;
340     const int samples_to_write = btstack_min(num_samples, num_samples_to_write);
341     int8_t audio_frame_out[24];
342 
343 
344     // memcpy(audio_frame_out, (int8_t*)(packet+3), 24);
345     btstack_cvsd_plc_process_data(&cvsd_plc_state, (int8_t *)(packet+3), num_samples, audio_frame_out);
346 
347     wav_writer_write_int8(samples_to_write, audio_frame_out);
348     num_samples_to_write -= samples_to_write;
349     if (num_samples_to_write == 0){
350         sco_demo_close();
351     }
352 #ifdef USE_PORTAUDIO
353     if (!pa_stream_started && btstack_ring_buffer_bytes_available(&ring_buffer) >= CVSD_PREBUFFER_BYTES){
354         /* -- start stream -- */
355         PaError err = Pa_StartStream(stream);
356         if (err != paNoError){
357             printf("Error starting the stream: \"%s\"\n",  Pa_GetErrorText(err));
358             return;
359         }
360         pa_stream_started = 1;
361     }
362     btstack_ring_buffer_write(&ring_buffer, (uint8_t *)audio_frame_out, samples_to_write);
363 #endif
364 }
365 
366 #endif
367 #endif
368 
369 void sco_demo_close(void){
370 #if SCO_DEMO_MODE == SCO_DEMO_MODE_SINE
371 #if defined(SCO_WAV_FILENAME) || defined(SCO_SBC_FILENAME)
372     wav_writer_close();
373     printf("SCO demo statistics: ");
374     if (negotiated_codec == HFP_CODEC_MSBC){
375         printf("Used mSBC with PLC, number of processed frames: \n - %d good frames, \n - %d zero frames, \n - %d bad frames.", decoder_state.good_frames_nr, decoder_state.zero_frames_nr, decoder_state.bad_frames_nr);
376     } else {
377         printf("Used CVSD with PLC, number of proccesed frames: \n - %d good frames, \n - %d bad frames.", cvsd_plc_state.good_frames_nr, cvsd_plc_state.bad_frames_nr);
378     }
379 #endif
380 #endif
381 
382 #ifdef HAVE_PORTAUDIO
383     if (pa_stream_started){
384         PaError err = Pa_StopStream(stream);
385         if (err != paNoError){
386             printf("Error stopping the stream: \"%s\"\n",  Pa_GetErrorText(err));
387             return;
388         }
389         pa_stream_started = 0;
390         err = Pa_CloseStream(stream);
391         if (err != paNoError){
392             printf("Error closing the stream: \"%s\"\n",  Pa_GetErrorText(err));
393             return;
394         }
395 
396         err = Pa_Terminate();
397         if (err != paNoError){
398             printf("Error terminating portaudio: \"%s\"\n",  Pa_GetErrorText(err));
399             return;
400         }
401     }
402 #endif
403 
404 #if SCO_DEMO_MODE == SCO_DEMO_MODE_SINE
405 #ifdef SCO_WAV_FILENAME
406 
407 #if 0
408     printf("SCO Demo: closing wav file\n");
409     if (negotiated_codec == HFP_CODEC_MSBC){
410         wav_writer_state_t * writer_state = &wav_writer_state;
411         if (!writer_state->wav_file) return;
412         rewind(writer_state->wav_file);
413         write_wav_header(writer_state->wav_file, writer_state->total_num_samples, btstack_sbc_decoder_num_channels(&decoder_state), btstack_sbc_decoder_sample_rate(&decoder_state),2);
414         fclose(writer_state->wav_file);
415         writer_state->wav_file = NULL;
416     }
417 #endif
418 #endif
419 #endif
420 }
421 
422 void sco_demo_set_codec(uint8_t codec){
423     if (negotiated_codec == codec) return;
424     negotiated_codec = codec;
425 #if SCO_DEMO_MODE == SCO_DEMO_MODE_SINE
426 #if defined(SCO_WAV_FILENAME) || defined(SCO_SBC_FILENAME)
427     if (negotiated_codec == HFP_CODEC_MSBC){
428         sco_demo_init_mSBC();
429     } else {
430         sco_demo_init_CVSD();
431     }
432 #endif
433 #endif
434 }
435 
436 void sco_demo_init(void){
437 
438 	// status
439 #if SCO_DEMO_MODE == SCO_DEMO_MODE_SINE
440 #ifdef HAVE_PORTAUDIO
441 	printf("SCO Demo: Sending sine wave, audio output via portaudio.\n");
442 #else
443 	printf("SCO Demo: Sending sine wave, hexdump received data.\n");
444 #endif
445 #endif
446 #if SCO_DEMO_MODE == SCO_DEMO_MODE_ASCII
447 	printf("SCO Demo: Sending ASCII blocks, print received data.\n");
448 #endif
449 #if SCO_DEMO_MODE == SCO_DEMO_MODE_COUNTER
450 	printf("SCO Demo: Sending counter value, hexdump received data.\n");
451 #endif
452 
453 #if SCO_DEMO_MODE != SCO_DEMO_MODE_SINE
454     hci_set_sco_voice_setting(0x03);    // linear, unsigned, 8-bit, transparent
455 #endif
456 
457 #if SCO_DEMO_MODE == SCO_DEMO_MODE_ASCII
458     phase = 'a';
459 #endif
460 }
461 
462 static void sco_report(void){
463     printf("SCO: sent %u, received %u\n", count_sent, count_received);
464 }
465 
466 void sco_demo_send(hci_con_handle_t sco_handle){
467 
468     if (!sco_handle) return;
469 
470     const int sco_packet_length = 24 + 3; // hci_get_sco_packet_length();
471     const int sco_payload_length = sco_packet_length - 3;
472 
473     hci_reserve_packet_buffer();
474     uint8_t * sco_packet = hci_get_outgoing_packet_buffer();
475     // set handle + flags
476     little_endian_store_16(sco_packet, 0, sco_handle);
477     // set len
478     sco_packet[2] = sco_payload_length;
479     const int audio_samples_per_packet = sco_payload_length;    // for 8-bit data. for 16-bit data it's /2
480 
481 #if SCO_DEMO_MODE == SCO_DEMO_MODE_SINE
482     if (negotiated_codec == HFP_CODEC_MSBC){
483 
484         if (hfp_msbc_num_bytes_in_stream() < sco_payload_length){
485             log_error("mSBC stream is empty.");
486         }
487         hfp_msbc_read_from_stream(sco_packet + 3, sco_payload_length);
488         if (msbc_file_out){
489             // log outgoing mSBC data for testing
490             fwrite(sco_packet + 3, sco_payload_length, 1, msbc_file_out);
491         }
492 
493         sco_demo_fill_audio_frame();
494     } else {
495         sco_demo_sine_wave_int8(audio_samples_per_packet, (int8_t *) (sco_packet+3));
496     }
497 #else
498 #if SCO_DEMO_MODE == SCO_DEMO_MODE_ASCII
499     memset(&sco_packet[3], phase++, audio_samples_per_packet);
500     if (phase > 'z') phase = 'a';
501 #else
502     int j;
503     for (j=0;j<audio_samples_per_packet;j++){
504         sco_packet[3+j] = phase++;
505     }
506 #endif
507 #endif
508 
509     hci_send_sco_packet_buffer(sco_packet_length);
510 
511     // request another send event
512     hci_request_sco_can_send_now_event();
513 
514     count_sent++;
515     if ((count_sent % SCO_REPORT_PERIOD) == 0) sco_report();
516 }
517 
518 /**
519  * @brief Process received data
520  */
521 void sco_demo_receive(uint8_t * packet, uint16_t size){
522 
523     dump_data = 1;
524 
525     count_received++;
526     // if ((count_received % SCO_REPORT_PERIOD) == 0) sco_report();
527 
528 
529 #if SCO_DEMO_MODE == SCO_DEMO_MODE_SINE
530 #ifdef SCO_WAV_FILENAME
531     if (negotiated_codec == HFP_CODEC_MSBC){
532         sco_demo_receive_mSBC(packet, size);
533     } else {
534         sco_demo_receive_CVSD(packet, size);
535     }
536     dump_data = 0;
537 #endif
538 #endif
539 
540     if (packet[1] & 0x30){
541         printf("SCO CRC Error: %x - data: ", (packet[1] & 0x30) >> 4);
542         log_info("SCO CRC Error: %x - data: ", (packet[1] & 0x30) >> 4);
543         printf_hexdump(&packet[3], size-3);
544 
545         return;
546     }
547     if (dump_data){
548         printf("data: ");
549 #if SCO_DEMO_MODE == SCO_DEMO_MODE_ASCII
550         int i;
551         for (i=3;i<size;i++){
552             printf("%c", packet[i]);
553         }
554         printf("\n");
555         dump_data = 0;
556 #else
557         printf_hexdump(&packet[3], size-3);
558 #endif
559     }
560 }
561