fix bug when loading a correct icd *after* rejecting one
[ocl-icd] / ocl_icd_loader.c
1 /**
2 Copyright (c) 2012, Brice Videau <brice.videau@imag.fr>
3 Copyright (c) 2012, Vincent Danjean <Vincent.Danjean@ens-lyon.org>
4 All rights reserved.
5       
6 Redistribution and use in source and binary forms, with or without
7 modification, are permitted provided that the following conditions are met:
8     
9 1. Redistributions of source code must retain the above copyright notice, this
10    list of conditions and the following disclaimer.
11 2. Redistributions in binary form must reproduce the above copyright notice,
12    this list of conditions and the following disclaimer in the documentation
13    and/or other materials provided with the distribution.
14         
15 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
16 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
19 ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
22 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
24 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
27 #include <dirent.h>
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <dlfcn.h>
32 #include <sys/types.h>
33 #include <sys/stat.h>
34 #include <fcntl.h>
35 #include <errno.h>
36 #include "config.h"
37 #ifdef USE_PTHREAD
38 #  include <pthread.h>
39 #endif
40 #pragma GCC diagnostic push
41 #  pragma GCC diagnostic ignored "-Wcpp"
42 #  define CL_USE_DEPRECATED_OPENCL_1_1_APIS
43 #  include <CL/opencl.h>
44 #pragma GCC diagnostic pop
45
46 #pragma GCC visibility push(hidden)
47
48 #include "ocl_icd_loader.h"
49 #define DEBUG_OCL_ICD_PROVIDE_DUMP_FIELD
50 #include "ocl_icd_debug.h"
51
52 #define ETC_OPENCL_VENDORS "/etc/OpenCL/vendors"
53
54 int debug_ocl_icd_mask=0;
55
56 typedef __typeof__(clGetExtensionFunctionAddress) *clGetExtensionFunctionAddress_fn;
57 typedef __typeof__(clGetPlatformInfo) *clGetPlatformInfo_fn;
58
59
60 struct vendor_icd {
61   cl_uint       num_platforms;
62   cl_uint       first_platform;
63   void *        dl_handle;
64   clGetExtensionFunctionAddress_fn ext_fn_ptr;
65 };
66
67 inline void dump_vendor_icd(const char* info, const struct vendor_icd *v) {
68   debug(D_DUMP, "%s %p={ num=%i, first=%i, handle=%p, f=%p}\n", info,
69         v, v->num_platforms, v->first_platform, v->dl_handle, v->ext_fn_ptr);
70 }
71
72 struct platform_icd {
73   char *         extension_suffix;
74   char *         version;
75   struct vendor_icd *vicd;
76   cl_platform_id pid;
77 };
78
79 struct vendor_icd *_icds=NULL;
80 struct platform_icd *_picds=NULL;
81 static cl_uint _num_icds = 0;
82 static cl_uint _num_picds = 0;
83
84 #ifdef DEBUG_OCL_ICD
85 #  define _clS(x) [-x] = #x
86 #  define MAX_CL_ERRORS (-CL_INVALID_DEVICE_PARTITION_COUNT)
87 static char const * const clErrorStr[MAX_CL_ERRORS+1] = {
88   _clS(CL_SUCCESS),
89   _clS(CL_DEVICE_NOT_FOUND),
90   _clS(CL_DEVICE_NOT_AVAILABLE),
91   _clS(CL_COMPILER_NOT_AVAILABLE),
92   _clS(CL_MEM_OBJECT_ALLOCATION_FAILURE),
93   _clS(CL_OUT_OF_RESOURCES),
94   _clS(CL_OUT_OF_HOST_MEMORY),
95   _clS(CL_PROFILING_INFO_NOT_AVAILABLE),
96   _clS(CL_MEM_COPY_OVERLAP),
97   _clS(CL_IMAGE_FORMAT_MISMATCH),
98   _clS(CL_IMAGE_FORMAT_NOT_SUPPORTED),
99   _clS(CL_BUILD_PROGRAM_FAILURE),
100   _clS(CL_MAP_FAILURE),
101   _clS(CL_MISALIGNED_SUB_BUFFER_OFFSET),
102   _clS(CL_EXEC_STATUS_ERROR_FOR_EVENTS_IN_WAIT_LIST),
103   _clS(CL_COMPILE_PROGRAM_FAILURE),
104   _clS(CL_LINKER_NOT_AVAILABLE),
105   _clS(CL_LINK_PROGRAM_FAILURE),
106   _clS(CL_DEVICE_PARTITION_FAILED),
107   _clS(CL_KERNEL_ARG_INFO_NOT_AVAILABLE),
108   _clS(CL_INVALID_VALUE),
109   _clS(CL_INVALID_DEVICE_TYPE),
110   _clS(CL_INVALID_PLATFORM),
111   _clS(CL_INVALID_DEVICE),
112   _clS(CL_INVALID_CONTEXT),
113   _clS(CL_INVALID_QUEUE_PROPERTIES),
114   _clS(CL_INVALID_COMMAND_QUEUE),
115   _clS(CL_INVALID_HOST_PTR),
116   _clS(CL_INVALID_MEM_OBJECT),
117   _clS(CL_INVALID_IMAGE_FORMAT_DESCRIPTOR),
118   _clS(CL_INVALID_IMAGE_SIZE),
119   _clS(CL_INVALID_SAMPLER),
120   _clS(CL_INVALID_BINARY),
121   _clS(CL_INVALID_BUILD_OPTIONS),
122   _clS(CL_INVALID_PROGRAM),
123   _clS(CL_INVALID_PROGRAM_EXECUTABLE),
124   _clS(CL_INVALID_KERNEL_NAME),
125   _clS(CL_INVALID_KERNEL_DEFINITION),
126   _clS(CL_INVALID_KERNEL),
127   _clS(CL_INVALID_ARG_INDEX),
128   _clS(CL_INVALID_ARG_VALUE),
129   _clS(CL_INVALID_ARG_SIZE),
130   _clS(CL_INVALID_KERNEL_ARGS),
131   _clS(CL_INVALID_WORK_DIMENSION),
132   _clS(CL_INVALID_WORK_GROUP_SIZE),
133   _clS(CL_INVALID_WORK_ITEM_SIZE),
134   _clS(CL_INVALID_GLOBAL_OFFSET),
135   _clS(CL_INVALID_EVENT_WAIT_LIST),
136   _clS(CL_INVALID_EVENT),
137   _clS(CL_INVALID_OPERATION),
138   _clS(CL_INVALID_GL_OBJECT),
139   _clS(CL_INVALID_BUFFER_SIZE),
140   _clS(CL_INVALID_MIP_LEVEL),
141   _clS(CL_INVALID_GLOBAL_WORK_SIZE),
142   _clS(CL_INVALID_PROPERTY),
143   _clS(CL_INVALID_IMAGE_DESCRIPTOR),
144   _clS(CL_INVALID_COMPILER_OPTIONS),
145   _clS(CL_INVALID_LINKER_OPTIONS),
146   _clS(CL_INVALID_DEVICE_PARTITION_COUNT)
147 };
148 #undef _clS
149 #endif
150
151 static char* _clerror2string (cl_int error) __attribute__((unused));
152 static char* _clerror2string (cl_int error) {
153 #ifdef DEBUG_OCL_ICD
154   if (-error > MAX_CL_ERRORS || error > 0) {
155     debug(D_WARN, "Unknown error code %d", error);
156     RETURN_STR("OpenCL Error");
157   }
158   const char *ret=clErrorStr[-error];
159   if (ret == NULL) {
160     debug(D_WARN, "Unknown error code %d", error);
161     RETURN_STR("OpenCL Error");
162   }
163   RETURN_STR(ret);
164 #else
165   static char number[15];
166   if (error==0) {
167     RETURN_STR("CL_SUCCESS");
168   }
169   snprintf(number, 15, "%i", error);
170   RETURN_STR(number);
171 #endif
172 }
173
174 static inline int _string_end_with_icd(const char* str) {
175   size_t len = strlen(str);
176   if( len<5 || strcmp(str + len - 4, ".icd" ) != 0 ) {
177     return 0;
178   }
179   return 1;
180 }
181
182 static inline int _string_with_slash(const char* str) {
183   return strchr(str, '/') != NULL;
184 }
185
186 static inline unsigned int _find_num_icds(DIR *dir) {
187   unsigned int num_icds = 0;
188   struct dirent *ent;
189   while( (ent=readdir(dir)) != NULL ){
190     if (_string_end_with_icd(ent->d_name)) {
191       num_icds++;
192     }
193   }
194   rewinddir(dir);
195   RETURN(num_icds);
196 }
197
198 static inline unsigned int _load_icd(int num_icds, const char* lib_path) {
199   unsigned int ret=0;
200   debug(D_LOG, "Loading ICD '%s'", lib_path);
201
202   _icds[num_icds].dl_handle = dlopen(lib_path, RTLD_LAZY|RTLD_LOCAL);//|RTLD_DEEPBIND);
203   if(_icds[num_icds].dl_handle != NULL) {
204     debug(D_LOG, "ICD[%i] loaded", num_icds);
205     ret=1;
206   } else {
207     debug(D_WARN, "error while dlopening the IDL: '%s',\n  => skipping ICD", dlerror());
208   }
209   return ret;
210 }
211
212 static inline unsigned int _open_driver(unsigned int num_icds,
213                                         const char*dir_path, const char*file_path) {
214   char * lib_path;
215   char * err;
216   unsigned int lib_path_length;
217   if (dir_path != NULL) {
218     lib_path_length = strlen(dir_path) + strlen(file_path) + 2;
219     lib_path = malloc(lib_path_length*sizeof(char));
220     sprintf(lib_path,"%s/%s", dir_path, file_path);
221   } else {
222     lib_path_length = strlen(file_path) + 1;
223     lib_path = malloc(lib_path_length*sizeof(char));
224     sprintf(lib_path,"%s", file_path);
225   }
226   debug(D_LOG, "Considering file '%s'", lib_path);
227   FILE *f = fopen(lib_path,"r");
228   free(lib_path);
229   if (f==NULL) {
230     RETURN(num_icds);
231   }
232   
233   fseek(f, 0, SEEK_END);
234   lib_path_length = ftell(f)+1;
235   fseek(f, 0, SEEK_SET);
236   if(lib_path_length == 1) {
237     debug(D_WARN, "File contents too short, skipping ICD");
238     fclose(f);
239     RETURN(num_icds);
240   }
241   lib_path = malloc(lib_path_length*sizeof(char));
242   err = fgets(lib_path, lib_path_length, f);
243   fclose(f);
244   if( err == NULL ) {
245     free(lib_path);
246     debug(D_WARN, "Error while loading file contents, skipping ICD");
247     RETURN(num_icds);
248   }
249
250   lib_path_length = strlen(lib_path);
251   
252   if( lib_path[lib_path_length-1] == '\n' )
253     lib_path[lib_path_length-1] = '\0';
254
255   num_icds += _load_icd(num_icds, lib_path);
256
257   free(lib_path);
258   RETURN(num_icds);
259 }
260
261 static inline unsigned int _open_drivers(DIR *dir, const char* dir_path) {
262   unsigned int num_icds = 0;
263   struct dirent *ent;
264   while( (ent=readdir(dir)) != NULL ){
265     if(! _string_end_with_icd(ent->d_name)) {
266       continue;
267     }
268     num_icds = _open_driver(num_icds, dir_path, ent->d_name);
269
270   }
271   RETURN(num_icds);
272 }
273
274 static void* _get_function_addr(void* dlh, clGetExtensionFunctionAddress_fn fn, const char*name) {
275   void *addr1;
276   debug(D_LOG,"Looking for function %s",name);
277   addr1=dlsym(dlh, name);
278   if (addr1 == NULL) {
279     debug(D_WARN, "Missing global symbol '%s' in ICD, should be skipped", name);
280   }
281   void* addr2=NULL;
282   if (fn) {
283     addr2=(*fn)(name);
284     if (addr2 == NULL) {
285       debug(D_WARN, "Missing function '%s' in ICD, should be skipped", name);
286     }
287 #ifdef DEBUG_OCL_ICD
288     if (addr1 && addr2 && addr1!=addr2) {
289       debug(D_WARN, "Function and symbol '%s' have different addresses!", name);
290     }
291 #endif
292   }
293   if (!addr2) addr2=addr1;
294   RETURN(addr2);
295 }
296
297 static int _allocate_platforms(int req) {
298   static cl_uint allocated=0;
299   debug(D_LOG,"Requesting allocation for %d platforms",req);
300   if (allocated - _num_picds < req) {
301     if (allocated==0) {
302       _picds=(struct platform_icd*)malloc(req*sizeof(struct platform_icd));
303     } else {
304       req = req - (allocated - _num_picds);
305       _picds=(struct platform_icd*)realloc(_picds, (allocated+req)*sizeof(struct platform_icd));
306     }
307     allocated += req;
308   }
309   RETURN(allocated - _num_picds);
310 }
311
312 static char* _malloc_clGetPlatformInfo(clGetPlatformInfo_fn plt_info_ptr,
313                  cl_platform_id pid, cl_platform_info cname, char* sname) {
314   cl_int error;
315   size_t param_value_size_ret;
316   error = plt_info_ptr(pid, cname, 0, NULL, &param_value_size_ret);
317   if (error != CL_SUCCESS) {
318     debug(D_WARN, "Error %s while requesting %s in platform %p",
319           _clerror2string(error), sname, pid);
320     return NULL;
321   }
322   char *param_value = (char *)malloc(sizeof(char)*param_value_size_ret);
323   if (param_value == NULL) {
324     debug(D_WARN, "Error in malloc while requesting %s in platform %p",
325           sname, pid);
326     return NULL;
327   }
328   error = plt_info_ptr(pid, cname, param_value_size_ret, param_value, NULL);
329   if (error != CL_SUCCESS){
330     free(param_value);
331     debug(D_WARN, "Error %s while requesting %s in platform %p",
332           _clerror2string(error), sname, pid);
333     return NULL;
334   }
335   RETURN_STR(param_value);
336 }
337
338 static inline void _find_and_check_platforms(cl_uint num_icds) {
339   cl_uint i;
340   _num_icds = 0;
341   for( i=0; i<num_icds; i++){
342     debug(D_LOG, "Checking ICD %i/%i", i, num_icds);
343     dump_vendor_icd("before looking for platforms", &_icds[i]);
344     struct vendor_icd *picd = &_icds[i];
345     void* dlh = _icds[i].dl_handle;
346     picd->ext_fn_ptr = _get_function_addr(dlh, NULL, "clGetExtensionFunctionAddress");
347     clIcdGetPlatformIDsKHR_fn plt_fn_ptr = 
348       _get_function_addr(dlh, picd->ext_fn_ptr, "clIcdGetPlatformIDsKHR");
349     clGetPlatformInfo_fn plt_info_ptr = 
350       _get_function_addr(dlh, picd->ext_fn_ptr, "clGetPlatformInfo");
351     if( picd->ext_fn_ptr == NULL
352         || plt_fn_ptr == NULL
353         || plt_info_ptr == NULL) {
354       debug(D_WARN, "Missing symbols in ICD, skipping it");
355       continue;
356     }
357     cl_uint num_platforms=0;
358     cl_int error;
359     error = (*plt_fn_ptr)(0, NULL, &num_platforms);
360     if( error != CL_SUCCESS || num_platforms == 0) {
361       debug(D_LOG, "No platform in ICD, skipping it");
362       continue;
363     }
364     cl_platform_id *platforms = (cl_platform_id *) malloc( sizeof(cl_platform_id) * num_platforms);
365     error = (*plt_fn_ptr)(num_platforms, platforms, NULL);
366     if( error != CL_SUCCESS ){
367       free(platforms);
368       debug(D_WARN, "Error in loading ICD platforms, skipping ICD");
369       continue;
370     }
371     cl_uint num_valid_platforms=0;
372     cl_uint j;
373     debug(D_LOG, "Try to load %d plateforms", num_platforms);
374     if (_allocate_platforms(num_platforms) < num_platforms) {
375       free(platforms);
376       debug(D_WARN, "Not enought platform allocated. Skipping ICD");
377       continue;
378     }
379     for(j=0; j<num_platforms; j++) {
380       debug(D_LOG, "Checking platform %i", j);
381       struct platform_icd *p=&_picds[_num_picds];
382       char *param_value=NULL;
383       p->extension_suffix=NULL;
384       p->vicd=&_icds[i];
385       p->pid=platforms[j];
386 #ifdef DEBUG_OCL_ICD
387       if (debug_ocl_icd_mask & D_DUMP) {
388         int log=debug_ocl_icd_mask & D_TRACE;
389         debug_ocl_icd_mask &= ~D_TRACE;
390         dump_platform(p->vicd->ext_fn_ptr, p->pid);
391         debug_ocl_icd_mask |= log;
392       }
393 #endif
394       {
395               /* Allow to workaround a bug in the Intel ICD used
396                * with optirun (search for NVidia Optimus for more info)
397                */
398               const char* str=getenv("OCL_ICD_ASSUME_ICD_EXTENSION");
399               if (! str || str[0]==0) {
400                       param_value=_malloc_clGetPlatformInfo(plt_info_ptr, p->pid, CL_PLATFORM_EXTENSIONS, "extensions");
401                       if (param_value == NULL){
402                               debug(D_WARN, "Skipping platform %i", j);
403                               continue;
404                       }
405                       debug(D_DUMP, "Supported extensions: %s", param_value);
406                       if( strstr(param_value, "cl_khr_icd") == NULL){
407                               free(param_value); 
408                               debug(D_WARN, "Missing khr extension in platform %i, skipping it", j);
409                               continue;
410                       }
411                       free(param_value);
412               }
413       }
414       param_value=_malloc_clGetPlatformInfo(plt_info_ptr, p->pid, CL_PLATFORM_ICD_SUFFIX_KHR, "suffix");
415       if (param_value == NULL){
416         debug(D_WARN, "Skipping platform %i", j);
417         continue;
418       }
419       p->extension_suffix = param_value;
420       debug(D_DUMP|D_LOG, "Extension suffix: %s", param_value);
421 #ifdef DEBUG_OCL_ICD
422       param_value=_malloc_clGetPlatformInfo(plt_info_ptr, p->pid, CL_PLATFORM_PROFILE, "profile");
423       if (param_value != NULL){
424         debug(D_DUMP, "Profile: %s", param_value);
425         free(param_value);
426       }
427       param_value=_malloc_clGetPlatformInfo(plt_info_ptr, p->pid, CL_PLATFORM_VERSION, "version");
428       p->version = param_value;
429       if (param_value != NULL){
430         debug(D_DUMP, "Version: %s", param_value);
431         free(param_value);
432       }
433       param_value=_malloc_clGetPlatformInfo(plt_info_ptr, p->pid, CL_PLATFORM_NAME, "name");
434       if (param_value != NULL){
435         debug(D_DUMP, "Name: %s", param_value);
436         free(param_value);
437       }
438       param_value=_malloc_clGetPlatformInfo(plt_info_ptr, p->pid, CL_PLATFORM_VENDOR, "vendor");
439       if (param_value != NULL){
440         debug(D_DUMP, "Vendor: %s", param_value);
441         free(param_value);
442       }
443 #endif
444       num_valid_platforms++;
445       _num_picds++;
446     }
447     if( num_valid_platforms != 0 ) {
448       if ( _num_icds != i ) {
449         picd->dl_handle = dlh;
450       }
451       dump_vendor_icd("after looking for platforms", &_icds[_num_icds]);
452       _num_icds++;
453       picd->num_platforms = num_valid_platforms;
454       _icds[i].first_platform = _num_picds - num_valid_platforms;
455     } else {
456       dlclose(dlh);
457     }
458     free(platforms);
459   }
460 }
461
462 static void __initClIcd( void ) {
463   debug_init();
464   cl_uint num_icds = 0;
465   int is_dir = 0;
466   DIR *dir;
467   const char* dir_path=getenv("OCL_ICD_VENDORS");
468   if (! dir_path || dir_path[0]==0) {
469     debug(D_DUMP, "OCL_ICD_VENDORS empty or not defined, using %s", ETC_OPENCL_VENDORS);
470     dir_path=ETC_OPENCL_VENDORS;
471     is_dir=1;
472   }
473   if (!is_dir) {
474     struct stat buf;
475     int ret=stat(dir_path, &buf);
476     if (ret != 0 && errno != ENOENT) {
477       debug(D_WARN, "Cannot stat '%s'. Aborting", dir_path);
478     }
479     if (ret == 0 && S_ISDIR(buf.st_mode)) {
480       is_dir=1;
481     }
482   }
483   
484   if (!is_dir) {
485     debug(D_LOG,"Only loading '%s' as an ICD", dir_path);
486     num_icds = 1;
487     dir=NULL;
488   } else {
489     debug(D_LOG,"Reading icd list from '%s'", dir_path);
490     dir = opendir(dir_path);
491     if(dir == NULL) {
492       if (errno == ENOTDIR) {
493         debug(D_DUMP, "%s is not a directory, trying to use it as a ICD libname",
494         dir_path);
495       }
496       goto abort;
497     }
498
499     num_icds = _find_num_icds(dir);
500     if(num_icds == 0) {
501       goto abort;
502     }
503   }
504
505   _icds = (struct vendor_icd*)malloc(num_icds * sizeof(struct vendor_icd));
506   if (_icds == NULL) {
507     goto abort;
508   }
509   
510   if (!is_dir) {
511     if (_string_end_with_icd(dir_path)) {
512       num_icds = 0;
513       if (! _string_with_slash(dir_path)) {
514         num_icds = _open_driver(0, ETC_OPENCL_VENDORS, dir_path);
515       }
516       if (num_icds == 0) {
517         num_icds = _open_driver(0, NULL, dir_path);
518       }
519     } else {
520       num_icds = _load_icd(0, dir_path);
521     }
522   } else {
523     num_icds = _open_drivers(dir, dir_path);
524   }
525   if(num_icds == 0) {
526     goto abort;
527   }
528
529   _find_and_check_platforms(num_icds);
530   if(_num_icds == 0){
531     goto abort;
532   }
533
534   if (_num_icds < num_icds) {
535     _icds = (struct vendor_icd*)realloc(_icds, _num_icds * sizeof(struct vendor_icd));
536   }
537   debug(D_WARN, "%d valid vendor(s)!", _num_icds);
538   return;
539  abort:
540   _num_icds = 0;
541   if (_icds) {
542     free(_icds);
543     _icds = NULL;
544   }
545   return;
546 }
547
548 #ifdef USE_PTHREAD
549 static pthread_once_t once_init = PTHREAD_ONCE_INIT;
550 #else
551 static int gard=0;
552 #endif
553 volatile static __thread int in_init = 0;
554 volatile static cl_uint _initialized = 0;
555
556 static inline void __attribute__((constructor)) _initClIcd( void ) {
557   if( _initialized )
558     return;
559 #ifdef USE_PTHREAD
560   if (in_init) {
561     /* probably reentrency */
562   } else {
563     in_init=1;
564     __sync_synchronize();
565     pthread_once(&once_init, &__initClIcd);
566     __sync_synchronize();
567     in_init=0;
568   }
569 #else
570   if (__sync_bool_compare_and_swap(&gard, 0, 1)) {
571     in_init=1;
572     __sync_synchronize();
573     __initClIcd();
574     __sync_synchronize();
575     in_init=0;
576   } else {
577     if (in_init) {
578       /* probably reentrency (could also be user threads). */
579     } else {
580       /* someone else started __initClIcd(). We wait until its end. */
581       debug(D_WARN, "Waiting end of init");
582       while (!_initialized) ;
583       debug(D_WARN, "Wait done");
584    }
585   }
586 #endif
587   _initialized = 1;
588 }
589
590 #pragma GCC visibility pop
591 #define hidden_alias(name) \
592   typeof(name) name##_hid __attribute__ ((alias (#name), visibility("hidden")))
593
594 typedef enum {
595   CL_ICDL_OCL_VERSION=1,
596   CL_ICDL_VERSION=2,
597   CL_ICDL_NAME=3,
598   CL_ICDL_VENDOR=4,
599 } cl_icdl_info;
600
601 static cl_int clGetICDLoaderInfoOCLICD(
602   cl_icdl_info     param_name,
603   size_t           param_value_size, 
604   void *           param_value,
605   size_t *         param_value_size_ret)
606 {
607   char cl_icdl_ocl_version[] = "OpenCL 1.2";
608   char cl_icdl_version[] = PACKAGE_VERSION;
609   char cl_icdl_name[] = PACKAGE_NAME;
610   char cl_icdl_vendor[] = "OCL Icd free software";
611
612   size_t size_string;
613   char * string_p;
614 #define oclcase(name, NAME) \
615   case CL_ICDL_##NAME: \
616     string_p = cl_icdl_##name; \
617     size_string = sizeof(cl_icdl_##name); \
618     break
619
620   switch ( param_name ) {
621     oclcase(ocl_version,OCL_VERSION);
622     oclcase(version,VERSION);
623     oclcase(name,NAME);
624     oclcase(vendor,VENDOR);
625     default:
626       return CL_INVALID_VALUE;
627       break;
628   }
629 #undef oclcase
630   if( param_value != NULL ) {
631     if( size_string > param_value_size )
632       return CL_INVALID_VALUE;
633     memcpy(param_value, string_p, size_string);
634   }
635   if( param_value_size_ret != NULL )
636     *param_value_size_ret = size_string;
637   return CL_SUCCESS;
638 }
639
640 CL_API_ENTRY void * CL_API_CALL
641 clGetExtensionFunctionAddress(const char * func_name) CL_API_SUFFIX__VERSION_1_0 {
642   debug_trace();
643   _initClIcd();
644   if( func_name == NULL )
645     return NULL;
646   cl_uint suffix_length;
647   cl_uint i;
648   void * return_value=NULL;
649   struct func_desc const * fn=&function_description[0];
650   int lenfn=strlen(func_name);
651   if (lenfn > 3 &&
652       (strcmp(func_name+lenfn-3, "KHR")==0 || strcmp(func_name+lenfn-3, "EXT")==0)) {
653     while (fn->name != NULL) {
654       if (strcmp(func_name, fn->name)==0)
655         RETURN(fn->addr);
656       fn++;
657     }
658   }
659   for(i=0; i<_num_picds; i++) {
660     suffix_length = strlen(_picds[i].extension_suffix);
661     if( suffix_length > strlen(func_name) )
662       continue;
663     if(strcmp(_picds[i].extension_suffix, &func_name[strlen(func_name)-suffix_length]) == 0)
664       RETURN((*_picds[i].vicd->ext_fn_ptr)(func_name));
665   }
666   if(strcmp(func_name, "clGetICDLoaderInfoOCLICD") == 0) {
667     return (void*)(void*(*)(void))(&clGetICDLoaderInfoOCLICD);
668   }
669   RETURN(return_value);
670 }
671 hidden_alias(clGetExtensionFunctionAddress);
672
673 CL_API_ENTRY cl_int CL_API_CALL
674 clGetPlatformIDs(cl_uint          num_entries,
675                  cl_platform_id * platforms,
676                  cl_uint *        num_platforms) CL_API_SUFFIX__VERSION_1_0 {
677   debug_trace();
678   _initClIcd();
679   if( platforms == NULL && num_platforms == NULL )
680     RETURN(CL_INVALID_VALUE);
681   if( num_entries == 0 && platforms != NULL )
682     RETURN(CL_INVALID_VALUE);
683   if( _num_icds == 0)
684     RETURN(CL_PLATFORM_NOT_FOUND_KHR);
685
686   cl_uint i;
687   if( num_platforms != NULL ){
688     *num_platforms = _num_picds;
689   }
690   if( platforms != NULL ) {
691     cl_uint n_platforms = _num_picds < num_entries ? _num_picds : num_entries;
692     for( i=0; i<n_platforms; i++) {
693       *(platforms++) = _picds[i].pid;
694     }
695   }
696   return CL_SUCCESS;
697 }
698 hidden_alias(clGetPlatformIDs);
699
700 CL_API_ENTRY cl_context CL_API_CALL
701 clCreateContext(const cl_context_properties *  properties ,
702                 cl_uint                        num_devices ,
703                 const cl_device_id *           devices ,
704                 void (CL_CALLBACK *  pfn_notify )(const char *, const void *, size_t, void *),
705                 void *                         user_data ,
706                 cl_int *                       errcode_ret ){
707   debug_trace();
708   _initClIcd();
709   cl_uint i=0;
710   if( properties != NULL){
711     while( properties[i] != 0 ) {
712       if( properties[i] == CL_CONTEXT_PLATFORM )
713         RETURN(((struct _cl_platform_id *) properties[i+1])
714           ->dispatch->clCreateContext(properties, num_devices, devices,
715                         pfn_notify, user_data, errcode_ret));
716       i += 2;
717     }
718   }
719   if(devices == NULL || num_devices == 0) {
720     if(errcode_ret) {
721       *errcode_ret = CL_INVALID_VALUE;
722     }
723     RETURN(NULL);
724   }
725   RETURN(((struct _cl_device_id *)devices[0])
726     ->dispatch->clCreateContext(properties, num_devices, devices,
727                   pfn_notify, user_data, errcode_ret));
728 }
729 hidden_alias(clCreateContext);
730
731 CL_API_ENTRY cl_context CL_API_CALL
732 clCreateContextFromType(const cl_context_properties *  properties ,
733                         cl_device_type                 device_type ,
734                         void (CL_CALLBACK *      pfn_notify )(const char *, const void *, size_t, void *),
735                         void *                         user_data ,
736                         cl_int *                       errcode_ret ){
737   debug_trace();
738   _initClIcd();
739   cl_uint i=0;
740   if( properties != NULL){
741     while( properties[i] != 0 ) {
742       if( properties[i] == CL_CONTEXT_PLATFORM )
743         if (properties[i+1] == 0) {
744           goto out;
745         }
746         return ((struct _cl_platform_id *) properties[i+1])
747           ->dispatch->clCreateContextFromType(properties, device_type,
748                         pfn_notify, user_data, errcode_ret);
749       i += 2;
750     }
751   } else {
752     if(_num_picds == 0) {
753       if(errcode_ret) {
754         *errcode_ret = CL_INVALID_VALUE;
755       }
756       RETURN(NULL);
757     }
758     const char *default_platform = getenv("OPENCL_ICD_DEFAULT_PLATFORM");
759     int num_default_platform;
760     char *end_scan;
761     if (! default_platform) {
762       num_default_platform = 0;
763     } else {
764       num_default_platform = strtol(default_platform, &end_scan, 10);
765       if (*default_platform == '\0' || *end_scan != '\0') {
766         goto out;
767       }
768     }
769     if (num_default_platform < 0 || num_default_platform >= _num_picds) {
770       goto out;
771     }
772     RETURN(_picds[num_default_platform].pid->dispatch->clCreateContextFromType
773         (properties, device_type, pfn_notify, user_data, errcode_ret));
774   }
775  out:
776   if(errcode_ret) {
777     *errcode_ret = CL_INVALID_PLATFORM;
778   }
779   RETURN(NULL);
780 }
781 hidden_alias(clCreateContextFromType);
782
783 CL_API_ENTRY cl_int CL_API_CALL
784 clGetGLContextInfoKHR(const cl_context_properties *  properties ,
785                       cl_gl_context_info             param_name ,
786                       size_t                         param_value_size ,
787                       void *                         param_value ,
788                       size_t *                       param_value_size_ret ){
789   debug_trace();
790   _initClIcd();
791   cl_uint i=0;
792   if( properties != NULL){
793     while( properties[i] != 0 ) {
794       if( properties[i] == CL_CONTEXT_PLATFORM )
795         RETURN(((struct _cl_platform_id *) properties[i+1])
796           ->dispatch->clGetGLContextInfoKHR(properties, param_name,
797                         param_value_size, param_value, param_value_size_ret));
798       i += 2;
799     }
800   }
801   RETURN(CL_INVALID_PLATFORM);
802 }
803 hidden_alias(clGetGLContextInfoKHR);
804
805 CL_API_ENTRY cl_int CL_API_CALL
806 clWaitForEvents(cl_uint              num_events ,
807                 const cl_event *     event_list ){
808   debug_trace();
809   if( num_events == 0 || event_list == NULL )
810     RETURN(CL_INVALID_VALUE);
811   RETURN(((struct _cl_event *)event_list[0])
812     ->dispatch->clWaitForEvents(num_events, event_list));
813 }
814 hidden_alias(clWaitForEvents);
815
816 CL_API_ENTRY cl_int CL_API_CALL
817 clUnloadCompiler( void ){
818   debug_trace();
819   RETURN(CL_SUCCESS);
820 }
821 hidden_alias(clUnloadCompiler);