1/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
2 *
3 * Redistribution and use in source and binary forms, with or without
4 * modification, are permitted provided that the following conditions are
5 * met:
6 *     * Redistributions of source code must retain the above copyright
7 *       notice, this list of conditions and the following disclaimer.
8 *     * Redistributions in binary form must reproduce the above
9 *       copyright notice, this list of conditions and the following
10 *       disclaimer in the documentation and/or other materials provided
11 *       with the distribution.
12 *     * Neither the name of The Linux Foundation nor the names of its
13 *       contributors may be used to endorse or promote products derived
14 *       from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
17 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
19 * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
20 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
23 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
24 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
25 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
26 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 *
28 */
29
30// System dependencies
31#include <dlfcn.h>
32#include <stdbool.h>
33#include <stdlib.h>
34#include <sys/time.h>
35
36// Camera dependencies
37#include "img_buffer.h"
38#include "mm_lib2d.h"
39
40
41#define ENABLE_OUTPUT_DUMP 1
42#define ALIGN4K 4032
43#define ALIGN(a, b) (((a) + (b)) & ~(b))
44
45
46/** DUMP_TO_FILE:
47 *  @filename: file name
48 *  @p_addr: address of the buffer
49 *  @len: buffer length
50 *
51 *  dump the image to the file
52 **/
53#define DUMP_TO_FILE(filename, p_addr, len) ({ \
54  size_t rc = 0; \
55  FILE *fp = fopen(filename, "w+"); \
56  if (fp) { \
57    rc = fwrite(p_addr, 1, len, fp); \
58    printf(" ] written size %zu \n",  __LINE__, len); \
59    fclose(fp); \
60  } else { \
61    printf(" ] open %s failed \n",  __LINE__, filename); \
62  } \
63})
64
65/** DUMP_TO_FILE2:
66 *  @filename: file name
67 *  @p_addr: address of the buffer
68 *  @len: buffer length
69 *
70 *  dump the image to the file if the memory is non-contiguous
71 **/
72#define DUMP_TO_FILE2(filename, p_addr1, len1, p_addr2, len2) ({ \
73  size_t rc = 0; \
74  FILE *fp = fopen(filename, "w+"); \
75  if (fp) { \
76    rc = fwrite(p_addr1, 1, len1, fp); \
77    rc = fwrite(p_addr2, 1, len2, fp); \
78    printf(" ] written %zu %zu \n",  __LINE__, len1, len2); \
79    fclose(fp); \
80  } else { \
81    printf(" ] open %s failed \n",  __LINE__, filename); \
82  } \
83})
84
85/** img_lib_buffert
86 * @ptr: handle to the imglib library
87 * @img_buffer_get: function pointer to img_buffer_get
88 * @img_buffer_release: function pointer to img_buffer_release
89 * @img_buffer_cacheops: function pointer to img_buffer_cacheops
90**/
91typedef struct {
92  void *ptr;
93  int (*img_buffer_get)(img_buf_type_t type, int heapid, int8_t cached, int length,
94    img_mem_handle_t *p_handle);
95  int (*img_buffer_release)(img_mem_handle_t *p_handle);
96  int (*img_buffer_cacheops)(img_mem_handle_t *p_handle, img_cache_ops_t ops,
97  img_mem_alloc_type_t mem_alloc_type);
98} img_lib_buffert;
99
100/** input_yuv_data
101 * @filename: input test filename
102 * @format: format of the input yuv frame
103 * @wdith: wdith of the input yuv frame
104 * @height: height of the input yuv frame
105 * @stride: stride of the input yuv frame
106 * @offset: offset to the yuv data in the input file
107**/
108typedef struct input_yuv_data_t {
109  char filename[512];
110  cam_format_t format;
111  int32_t wdith;
112  int32_t height;
113  int32_t stride;
114  int32_t offset;
115} input_yuv_data;
116
117input_yuv_data input_nv21[] = {
118  {"sample0_768x512.yuv",                             CAM_FORMAT_YUV_420_NV21, 768,  512,  768,  0},
119  {"sample1_3200x2400.yuv",                           CAM_FORMAT_YUV_420_NV21, 3200, 2400, 3200, 0},
120  {"sample2_1920x1080.yuv",                           CAM_FORMAT_YUV_420_NV21, 1920, 1080, 1920, 0},
121  {"sample3_3200x2400.yuv",                           CAM_FORMAT_YUV_420_NV21, 3200, 2400, 3200, 0},
122  {"sample4_4208x3120.yuv",                           CAM_FORMAT_YUV_420_NV21, 4208, 3120, 4208, 0},
123  {"sample5_1984x2592.yuv",                           CAM_FORMAT_YUV_420_NV21, 1984, 2592, 1984, 0},
124  {"sample6_4000_3000.yuv",                           CAM_FORMAT_YUV_420_NV21, 4000, 3000, 4000, 0},
125  {"sample7_3200_2400.yuv",                           CAM_FORMAT_YUV_420_NV21, 3200, 2400, 3200, 0},
126  {"sample8_3008_4000.yuv",                           CAM_FORMAT_YUV_420_NV21, 3008, 4000, 3008, 0},
127  {"sample9_5312x2988.yuv",                           CAM_FORMAT_YUV_420_NV21, 5312, 2988, 5312, 0},
128  {"sample10_4128x3096.yuv",                          CAM_FORMAT_YUV_420_NV21, 4128, 3096, 4128, 0},
129  {"sample11_4208x3120.yuv",                          CAM_FORMAT_YUV_420_NV21, 4208, 3120, 4208, 0},
130  {"sample12_3200x2400.yuv",                          CAM_FORMAT_YUV_420_NV21, 3200, 2400, 3200, 0},
131  {"sample13_width_1080_height_1440_stride_1088.yuv", CAM_FORMAT_YUV_420_NV21, 1080, 1440, 1088, 0},
132  {"sample14_width_1080_height_1920_stride_1088.yuv", CAM_FORMAT_YUV_420_NV21, 1080, 1920, 1088, 0},
133  {"sample15_width_1944_height_2592_stride_1984.yuv", CAM_FORMAT_YUV_420_NV21, 1944, 2592, 1984, 0},
134  {"sample16_width_3000_height_4000_stride_3008.yuv", CAM_FORMAT_YUV_420_NV21, 3000, 4000, 3008, 0},
135  {"sample17_width_3120_height_4208_stride_3136.yuv", CAM_FORMAT_YUV_420_NV21, 3120, 4208, 3136, 0},
136  {"sample18_width_3200_height_2400_stride_3200.yuv", CAM_FORMAT_YUV_420_NV21, 3200, 2400, 3200, 0},
137  {"sample19_width_1944_height_2592_stride_1984.yuv", CAM_FORMAT_YUV_420_NV21, 1944, 2592, 1984, 0},
138};
139
140// assuming buffer format is always ARGB
141void lib2d_dump_tga(void *addr, cam_format_t format, int width,
142  int height, int stride, char *fname)
143{
144  int i, j;
145  FILE *f;
146  unsigned char *pb = (unsigned char *)addr;
147  uint32_t *pd = (uint32_t *)addr;
148  int bpp = 32;
149
150  f = fopen(fname, "wb");
151  if (f) {
152    // header
153    fprintf(f, "%c%c%c%c", 0, 0, 2, 0);
154    fprintf(f, "%c%c%c%c", 0, 0, 0, 0);
155    fprintf(f, "%c%c%c%c", 0, 0, 0, 0);
156    fprintf(f, "%c%c%c%c", width & 0xff, width >> 8, height & 0xff, height >> 8);
157    fprintf(f, "%c%c", bpp, 32);
158
159    for (i = 0; i < height; i++) {
160      for (j = 0; j < width; j++) {
161        fprintf(f, "%c%c%c%c",
162          pd[(i*stride>>2)+j] & 0xff,           // b
163          (pd[(i*stride>>2)+j] >> 8) & 0xff,    // g
164          (pd[(i*stride>>2)+j] >> 16) & 0xff,   // r
165          (pd[(i*stride>>2)+j] >> 24) & 0xff);  // a
166      }
167    }
168    fclose(f);
169  }
170}
171
172/**
173 * Function: lib2d_test_client_cb
174 *
175 * Description: Callback that is called on completion of requested job.
176 *
177 * Input parameters:
178 *   userdata - App userdata
179 *   jobid - job id that is finished execution
180 *
181 * Return values:
182 *   MM_LIB2D_SUCCESS
183 *   MM_LIB2D_ERR_GENERAL
184 *
185 * Notes: none
186 **/
187lib2d_error lib2d_test_client_cb(void *userdata, int jobid)
188{
189  printf("%s %d, jobid=%d \n",  __LINE__, jobid);
190  return MM_LIB2D_SUCCESS;
191}
192
193/**
194 * Function: lib2d_test_load_input_yuv_data
195 *
196 * Description: Loads yuv data from input file.
197 *
198 * Input parameters:
199 *   fileName - input yuv filename
200 *   offset - offset to the yuv data in the input file
201 *   y_size - y plane size in input yuv file
202 *   crcb_size - crcb plane size in input yuv file
203 *   crcb_offset - crcb offset in the memory at
204 *       which crcb data need to be loaded
205 *   addr - y plane memory address where y plane
206 *       data need to be loaded.
207 *
208 * Return values:
209 *   MM_LIB2D_SUCCESS
210 *   MM_LIB2D_ERR_GENERAL
211 *
212 * Notes: none
213 **/
214lib2d_error lib2d_test_load_input_yuv_data(char *fileName, int offset,
215    int32_t y_size, int32_t crcb_size, int32_t crcb_offset,
216    void *addr)
217{
218  size_t i;
219  FILE  *fp       = 0;
220  void  *y_ptr    = addr;
221  void  *crcb_ptr = (uint8_t *)addr + crcb_offset;
222
223  printf("y_ptr=%p, crcb_ptr=%p \n", y_ptr, crcb_ptr);
224
225  fp = fopen(fileName, "rb");
226  if(fp) {
227    if(offset) {
228      fseek(fp, offset, SEEK_SET);
229    }
230    i = fread(y_ptr, 1, y_size, fp);
231    i = fread(crcb_ptr, 1, crcb_size, fp);
232
233    fclose( fp );
234  } else {
235    printf("failed to open file %s \n", fileName);
236    return MM_LIB2D_ERR_GENERAL;
237  }
238
239  return MM_LIB2D_SUCCESS;
240}
241
242/**
243 * Function: lib2d_test_load_input_yuv_data
244 *
245 * Description: Loads yuv data from input file.
246 *
247 * Input parameters:
248 *   fileName - input yuv filename
249 *   offset - offset to the yuv data in the input file
250 *   input_yuv_stride - y plane stride in input yuv file
251 *   y_plane_stride - y plane stride in buffer memory
252 *   height - height of yuv image
253 *   crcb_offset - crcb offset in the memory at
254 *       which crcb data need to be loaded
255 *   addr - y plane memory address where y plane
256 *       data need to be loaded.
257 *
258 * Return values:
259 *   MM_LIB2D_SUCCESS
260 *   MM_LIB2D_ERR_GENERAL
261 *
262 * Notes: none
263 **/
264lib2d_error lib2d_test_load_input_yuv_data_linebyline(char *fileName,
265    int offset, int32_t input_yuv_stride, int32_t y_plane_stride,
266    int32_t height, int32_t crcb_offset, void *addr)
267{
268  size_t i;
269  FILE  *fp       = 0;
270  void  *y_ptr    = addr;
271  void  *crcb_ptr = (uint8_t *)addr + crcb_offset;
272
273  printf("y_ptr=%p, crcb_ptr=%p \n", y_ptr, crcb_ptr);
274
275  fp = fopen(fileName, "rb");
276  if(fp) {
277    if(offset) {
278      fseek(fp, offset, SEEK_SET);
279    }
280    if (input_yuv_stride == y_plane_stride) {
281      //load y plane
282      i = fread(y_ptr, 1, (input_yuv_stride * height), fp);
283      // load UV plane
284      i = fread(crcb_ptr, 1, (input_yuv_stride * height / 2), fp);
285    } else {
286      int line = 0;
287      // load Y plane
288      for (line = 0;line < height; line++) {
289        i = fread(y_ptr, 1, input_yuv_stride, fp);
290        y_ptr = (void *)((uint8_t *)y_ptr + y_plane_stride);
291      }
292      for (line = 0;line < height; line++) {
293        i = fread(crcb_ptr, 1, input_yuv_stride, fp);
294        crcb_ptr = (void *)((uint8_t *)crcb_ptr + y_plane_stride);
295      }
296    }
297
298    fclose( fp );
299  } else {
300    printf("failed to open file %s \n", fileName);
301    return MM_LIB2D_ERR_GENERAL;
302  }
303
304  return MM_LIB2D_SUCCESS;
305}
306
307/**
308 * Function: main
309 *
310 * Description: main function for execution
311 *
312 * Input parameters:
313 *   argc - no.of input arguments
314 *   argv - list of arguments
315 *
316 * Return values:
317 *   0 on success
318 *   -1 on failure
319 *
320 * Notes: none
321 **/
322int main(int32_t argc, const char * argv[])
323{
324  void            *lib2d_handle       = NULL;
325  lib2d_error      lib2d_err          = MM_LIB2D_SUCCESS;
326  mm_lib2d_buffer  src_buffer         = {0};
327  mm_lib2d_buffer  dst_buffer         = {0};
328  int8_t           ret                = IMG_SUCCESS;
329  int32_t          width              = 0;
330  int32_t          height             = 0;
331  int32_t          input_yuv_stride   = 0;
332  int32_t          stride             = 0;
333  int32_t          y_plane_stride     = 0;
334  int32_t          crcb_plane_stride  = 0;
335  int32_t          y_plane_size       = 0;
336  int32_t          y_plane_size_align = 0;
337  int32_t          crcb_plane_size    = 0;
338  int32_t          yuv_size           = 0;
339  int32_t          rgb_size           = 0;
340  img_mem_handle_t m_yuv_memHandle    = { 0 };
341  img_mem_handle_t m_rgb_memHandle    = { 0 };
342  char             filename_in[512]   = { 0 };
343  char             filename_out[512]  = { 0 };
344  char             filename_raw[512]  = { 0 };
345  int32_t          offset             = 0;
346  unsigned int     total_tests        = 1;
347  cam_format_t     format             = CAM_FORMAT_YUV_420_NV21;
348  unsigned int     index;
349  const char      *filename;
350
351  // Open Imglib library and get the function pointers for
352  // buffer allocation, free, cacheops
353  img_lib_buffert  img_lib;
354  img_lib.ptr = dlopen("libmmcamera_imglib.so", RTLD_NOW);
355  if (!img_lib.ptr) {
356    printf("%s ERROR: couldn't dlopen libmmcamera_imglib.so: %s",
357       dlerror());
358    return -1;
359  }
360
361  /* Get function pointer for functions to allocate ion memory */
362  *(void **)&img_lib.img_buffer_get =
363      dlsym(img_lib.ptr, "img_buffer_get");
364  *(void **)&img_lib.img_buffer_release =
365      dlsym(img_lib.ptr, "img_buffer_release");
366  *(void **)&img_lib.img_buffer_cacheops =
367      dlsym(img_lib.ptr, "img_buffer_cacheops");
368
369  /* Validate function pointers */
370  if ((img_lib.img_buffer_get == NULL) ||
371    (img_lib.img_buffer_release == NULL) ||
372    (img_lib.img_buffer_cacheops == NULL)) {
373    printf(" ERROR mapping symbols from libmmcamera_imglib.so");
374    dlclose(img_lib.ptr);
375    return -1;
376  }
377
378  lib2d_err = mm_lib2d_init(MM_LIB2D_SYNC_MODE, CAM_FORMAT_YUV_420_NV21,
379    CAM_FORMAT_8888_ARGB, &lib2d_handle);
380  if ((lib2d_err != MM_LIB2D_SUCCESS) || (lib2d_handle == NULL)) {
381    return -1;
382  }
383
384  bool run_default = FALSE;
385
386  if ( argc == 7) {
387    filename         = argv[1];
388    width            = (uint32_t)atoi(argv[2]);
389    height           = (uint32_t)atoi(argv[3]);
390    input_yuv_stride = (uint32_t)atoi(argv[4]);
391    offset           = (uint32_t)atoi(argv[5]);
392    format           = (uint32_t)atoi(argv[6]);
393    run_default      = TRUE;
394    printf("Running user provided conversion \n");
395  }
396  else {
397    total_tests = sizeof(input_nv21)/sizeof(input_yuv_data);
398    printf("usage: <binary> <filname> <width> <height> "
399      "<stride> <offset> <format> \n");
400  }
401
402  for (index = 0; index < total_tests; index++)
403  {
404    if(run_default == FALSE) {
405      filename         = input_nv21[index].filename;
406      width            = input_nv21[index].wdith;
407      height           = input_nv21[index].height;
408      input_yuv_stride = input_nv21[index].stride;
409      offset           = input_nv21[index].offset;
410      format           = input_nv21[index].format;
411    }
412
413    snprintf(filename_in, 512, "/data/lib2d/input/%s", filename);
414    snprintf(filename_out, 512, "/data/lib2d/output/%s.tga", filename);
415    snprintf(filename_raw, 512, "/data/lib2d/output/%s.rgba", filename);
416
417    printf("-----------------Running test=%d/%d------------------------- \n",
418      index+1, total_tests);
419    printf("filename=%s, full path=%s, width=%d, height=%d, stride=%d \n",
420      filename, filename_in, width, height, stride);
421
422    // Allocate NV12 buffer
423    y_plane_stride     = ALIGN(width, 32);
424    y_plane_size       = y_plane_stride * height;
425    y_plane_size_align = ALIGN(y_plane_size, ALIGN4K);
426    crcb_plane_stride  = y_plane_stride;
427    crcb_plane_size    = crcb_plane_stride * height / 2;
428    yuv_size           = y_plane_size_align + crcb_plane_size;
429    ret = img_lib.img_buffer_get(IMG_BUFFER_ION_IOMMU, -1, TRUE,
430          yuv_size, &m_yuv_memHandle);
431    if (ret != IMG_SUCCESS) {
432      printf(" ] Error, img buf get failed \n");
433      goto deinit;
434    }
435
436    printf("%s %d yuv buffer properties : w=%d, h=%d, y_stride=%d, "
437      "crcb_stride=%d, y_size=%d, crcb_size=%d, yuv_size=%d, "
438      "crcb_offset=%d \n",
439       __LINE__,
440      width, height, y_plane_stride, crcb_plane_stride, y_plane_size,
441      crcb_plane_size, yuv_size, y_plane_size_align);
442    printf("%s %d yuv buffer properties : fd=%d, ptr=%p, size=%d \n",
443       __LINE__, m_yuv_memHandle.fd, m_yuv_memHandle.vaddr,
444      m_yuv_memHandle.length);
445
446    // Allocate ARGB buffer
447    stride   = width * 4;
448    stride   = ALIGN(stride, 32);
449    rgb_size = stride * height;
450    ret = img_lib.img_buffer_get(IMG_BUFFER_ION_IOMMU, -1, TRUE,
451          rgb_size, &m_rgb_memHandle);
452    if (ret != IMG_SUCCESS) {
453      printf(" ] Error, img buf get failed");
454      img_lib.img_buffer_release(&m_yuv_memHandle);
455      goto deinit;
456    }
457
458    printf("%s %d rgb buffer properties : w=%d, h=%d, stride=%d, size=%d \n",
459       __LINE__, width, height, stride, rgb_size);
460    printf("%s %d rgb buffer properties : fd=%d, ptr=%p, size=%d \n",
461       __LINE__, m_rgb_memHandle.fd, m_rgb_memHandle.vaddr,
462      m_rgb_memHandle.length);
463
464#if 0
465    lib2d_err = lib2d_test_load_input_yuv_data(filename_in, offset,
466      (input_yuv_stride * height), (input_yuv_stride * height / 2), y_plane_size_align,
467      m_yuv_memHandle.vaddr);
468    if (lib2d_err != MM_LIB2D_SUCCESS) {
469      printf(" ] Error loading the input buffer \n");
470      goto release;
471    }
472#else
473    lib2d_err = lib2d_test_load_input_yuv_data_linebyline(filename_in, offset,
474      input_yuv_stride, y_plane_stride,height, y_plane_size_align,
475      m_yuv_memHandle.vaddr);
476    if (lib2d_err != MM_LIB2D_SUCCESS) {
477      printf(" ] Error loading the input buffer \n");
478      goto release;
479    }
480#endif
481    // Setup source buffer
482    src_buffer.buffer_type = MM_LIB2D_BUFFER_TYPE_YUV;
483    src_buffer.yuv_buffer.fd      = m_yuv_memHandle.fd;
484    src_buffer.yuv_buffer.format  = format;
485    src_buffer.yuv_buffer.width   = width;
486    src_buffer.yuv_buffer.height  = height;
487    src_buffer.yuv_buffer.plane0  = m_yuv_memHandle.vaddr;
488    src_buffer.yuv_buffer.stride0 = y_plane_stride;
489    src_buffer.yuv_buffer.plane1  = (int8_t *)m_yuv_memHandle.vaddr +
490                                    y_plane_size_align;
491    src_buffer.yuv_buffer.stride1 = crcb_plane_stride;
492
493    // Setup dst buffer
494    dst_buffer.buffer_type = MM_LIB2D_BUFFER_TYPE_RGB;
495    dst_buffer.rgb_buffer.fd     = m_rgb_memHandle.fd;
496    dst_buffer.rgb_buffer.format = CAM_FORMAT_8888_ARGB;
497    dst_buffer.rgb_buffer.width  = width;
498    dst_buffer.rgb_buffer.height = height;
499    dst_buffer.rgb_buffer.buffer = m_rgb_memHandle.vaddr;
500    dst_buffer.rgb_buffer.stride = stride;
501
502    img_lib.img_buffer_cacheops(&m_yuv_memHandle,
503      IMG_CACHE_CLEAN_INV, IMG_INTERNAL);
504
505    lib2d_err = mm_lib2d_start_job(lib2d_handle, &src_buffer, &dst_buffer,
506      index, NULL, lib2d_test_client_cb, 0);
507    if (lib2d_err != MM_LIB2D_SUCCESS) {
508      printf(" ] Error in mm_lib2d_start_job \n");
509      goto release;
510    }
511
512    img_lib.img_buffer_cacheops(&m_rgb_memHandle,
513      IMG_CACHE_CLEAN_INV, IMG_INTERNAL);
514
515#ifdef ENABLE_OUTPUT_DUMP
516    // Dump output files
517    // snprintf(filename_in, 512, "/data/lib2d/output/%s", filename);
518    // DUMP_TO_FILE2(filename_in, src_buffer.yuv_buffer.plane0, y_plane_size, src_buffer.yuv_buffer.plane1, crcb_plane_size);
519    // DUMP_TO_FILE(filename_raw, dst_buffer.rgb_buffer.buffer, rgb_size);
520    printf("Dumping output file %s \n", filename_out);
521    lib2d_dump_tga(dst_buffer.rgb_buffer.buffer, 1,
522      width, height, stride, filename_out);
523#endif
524
525    img_lib.img_buffer_release(&m_rgb_memHandle);
526    img_lib.img_buffer_release(&m_yuv_memHandle);
527  }
528
529  mm_lib2d_deinit(lib2d_handle);
530
531  return 0;
532
533release:
534  img_lib.img_buffer_release(&m_rgb_memHandle);
535  img_lib.img_buffer_release(&m_yuv_memHandle);
536deinit:
537  mm_lib2d_deinit(lib2d_handle);
538  printf("%s %d some error happened, tests completed = %d/%d \n",
539     __LINE__, index - 1, total_tests);
540  return -1;
541}
542
543
544