1 /*
2 * Copyright (c) 2022 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include <errno.h>
17 #include <linux/netlink.h>
18 #include <poll.h>
19 #include <stdlib.h>
20 #include <sys/socket.h>
21 #include <unistd.h>
22
23 #include "devmgr_service.h"
24 #include "hdf_dlist.h"
25 #include "hdf_log.h"
26 #include "osal_mem.h"
27 #include "osal_thread.h"
28 #include "osal_time.h"
29 #include "securec.h"
30
31 #define HDF_LOG_TAG devmgr_uevent
32
33 #define UEVENT_SOCKET_BUFF_SIZE (256 * 1024)
34
35 #ifndef LINE_MAX
36 #define LINE_MAX 4096
37 #endif
38
39 #define ACTION_STR "ACTION=="
40 #define PNP_EVENT_STR "HDF_PNP_EVENT="
41 #define KEY_VALUE_DELIMITER "==\""
42 #define KEY_DELIMITER "\","
43 #define UEVENT_DELIMITER "="
44
45 enum DEVMGR_ACTION_TYPE {
46 DEVMGR_ACTION_LOAD,
47 DEVMGR_ACTION_UNLOAD,
48 DEVMGR_ACTION_MAX,
49 };
50
51 struct DevMgrUeventRuleCfg {
52 char *serviceName;
53 enum DEVMGR_ACTION_TYPE action;
54 struct DListHead matchKeyList;
55 struct DListHead entry;
56 };
57
58 struct DevMgrMatchKey {
59 char *key;
60 char *value;
61 struct DListHead entry;
62 };
63
DevMgrUeventRuleCfgList(void)64 static struct DListHead *DevMgrUeventRuleCfgList(void)
65 {
66 static struct DListHead ruleCfgList;
67 static bool initFlag = false;
68
69 if (!initFlag) {
70 DListHeadInit(&ruleCfgList);
71 initFlag = true;
72 }
73
74 return &ruleCfgList;
75 }
76
DevMgrUeventReleaseKeyList(struct DListHead * keyList)77 static void DevMgrUeventReleaseKeyList(struct DListHead *keyList)
78 {
79 struct DevMgrMatchKey *matchKey = NULL;
80 struct DevMgrMatchKey *matchKeyTmp = NULL;
81
82 if (keyList == NULL) {
83 HDF_LOGE("keyList is null");
84 return;
85 }
86 DLIST_FOR_EACH_ENTRY_SAFE(matchKey, matchKeyTmp, keyList, struct DevMgrMatchKey, entry) {
87 DListRemove(&matchKey->entry);
88 OsalMemFree(matchKey->key);
89 OsalMemFree(matchKey->value);
90 OsalMemFree(matchKey);
91 }
92 }
93
DevMgrUeventReleaseRuleCfgList(void)94 static void DevMgrUeventReleaseRuleCfgList(void)
95 {
96 struct DevMgrUeventRuleCfg *ruleCfg = NULL;
97 struct DevMgrUeventRuleCfg *ruleCfgTmp = NULL;
98 struct DListHead *ruleCfgList = DevMgrUeventRuleCfgList();
99
100 DLIST_FOR_EACH_ENTRY_SAFE(ruleCfg, ruleCfgTmp, ruleCfgList, struct DevMgrUeventRuleCfg, entry) {
101 DListRemove(&ruleCfg->entry);
102 DevMgrUeventReleaseKeyList(&ruleCfg->matchKeyList);
103 OsalMemFree(ruleCfg->serviceName);
104 OsalMemFree(ruleCfg);
105 }
106 }
107
108 // replace substrings "\n" or "\r" in a string with characters '\n' or '\r'
DevMgrUeventReplaceLineFeed(char * str,const char * subStr,char c)109 static void DevMgrUeventReplaceLineFeed(char *str, const char *subStr, char c)
110 {
111 char *ptr = NULL;
112
113 while ((ptr = strstr(str, subStr)) != NULL) {
114 ptr[0] = c;
115 uint32_t i = 1;
116 if (strlen(ptr) < 1 || strlen(ptr) > SIZE_MAX) {
117 HDF_LOGE("strlen(ptr) overflows");
118 return;
119 }
120 const size_t len = strlen(ptr) - 1;
121 for (; i < len; i++) {
122 ptr[i] = ptr[i + 1];
123 }
124 ptr[i] = '\0';
125 }
126
127 return;
128 }
129
DevMgrUeventParseKeyValue(char * str,struct DevMgrMatchKey * matchKey,const char * sep)130 static int32_t DevMgrUeventParseKeyValue(char *str, struct DevMgrMatchKey *matchKey, const char *sep)
131 {
132 char *subPtr = strstr(str, sep);
133 if (subPtr == NULL) {
134 if (strstr(str, "@/") == NULL) {
135 HDF_LOGE("parse Key value failed:[%{public}s]", str);
136 }
137 return HDF_FAILURE;
138 }
139
140 if (strlen(subPtr) < (strlen(sep) + 1)) {
141 HDF_LOGE("value part invalid:[%{public}s]", str);
142 return HDF_FAILURE;
143 }
144
145 char *value = subPtr + strlen(sep);
146 *subPtr = '\0';
147 HDF_LOGD("key:%{private}s,value:[%{private}s]", str, value);
148 matchKey->key = strdup(str);
149 matchKey->value = strdup(value);
150 if (matchKey->key == NULL || matchKey->value == NULL) {
151 HDF_LOGE("invalid param : matchKey->key or matchKey->value");
152 return HDF_FAILURE;
153 }
154
155 return HDF_SUCCESS;
156 }
157
DevMgrUeventCheckRuleValid(const char * line)158 static bool DevMgrUeventCheckRuleValid(const char *line)
159 {
160 if (strlen(line) < strlen(PNP_EVENT_STR) || line[0] == '#') {
161 return false;
162 }
163
164 if (strstr(line, PNP_EVENT_STR) == NULL) {
165 HDF_LOGE("%{public}s invalid rule: %{public}s", __func__, line);
166 return false;
167 }
168
169 return true;
170 }
171
DevMgrUeventParseMatchKey(char * subStr,struct DListHead * matchKeyList)172 static int32_t DevMgrUeventParseMatchKey(char *subStr, struct DListHead *matchKeyList)
173 {
174 if (subStr == NULL || strlen(subStr) == 0) {
175 HDF_LOGE("match key invalid [%{public}s]", subStr);
176 return HDF_FAILURE;
177 }
178
179 struct DevMgrMatchKey *matchKey = (struct DevMgrMatchKey *)OsalMemCalloc(sizeof(*matchKey));
180 if (matchKey == NULL) {
181 HDF_LOGE("%{public}s OsalMemCalloc matchKey failed", __func__);
182 return HDF_FAILURE;
183 }
184 if (matchKey->value == NULL || matchKey->key == NULL) {
185 HDF_LOGW("%{public}s OsalMemCalloc matchKey->value or matchKey->key failed", __func__);
186 }
187 DListHeadInit(&matchKey->entry);
188
189 if (DevMgrUeventParseKeyValue(subStr, matchKey, KEY_VALUE_DELIMITER) == HDF_SUCCESS) {
190 DevMgrUeventReplaceLineFeed(matchKey->value, "\\n", '\n');
191 DevMgrUeventReplaceLineFeed(matchKey->value, "\\r", '\r');
192 DListInsertTail(&matchKey->entry, matchKeyList);
193 return HDF_SUCCESS;
194 } else {
195 OsalMemFree(matchKey);
196 return HDF_FAILURE;
197 }
198 }
199
DevMgrUeventParseHdfEvent(char * subStr,struct DevMgrUeventRuleCfg * ruleCfg)200 static int32_t DevMgrUeventParseHdfEvent(char *subStr, struct DevMgrUeventRuleCfg *ruleCfg)
201 {
202 if (strncmp(subStr, PNP_EVENT_STR, strlen(PNP_EVENT_STR)) != 0 || strlen(subStr) < strlen(PNP_EVENT_STR) + 1) {
203 HDF_LOGE("parse hdf event %{public}s failed", subStr);
204 return HDF_FAILURE;
205 }
206
207 char *event = subStr + strlen(PNP_EVENT_STR);
208 char *ptr = strchr(event, ':');
209 if (ptr == NULL) {
210 HDF_LOGE("parse event %{public}s fail, no action", subStr);
211 return HDF_FAILURE;
212 }
213
214 if (strlen(ptr) < strlen(":load")) {
215 HDF_LOGE("event action part invalid [%{public}s]", ptr);
216 return HDF_FAILURE;
217 }
218
219 *ptr = '\0';
220 char *action = ptr + 1;
221
222 // replace line feed
223 ptr = strchr(action, '\n');
224 if (ptr != NULL) {
225 *ptr = '\0';
226 }
227
228 // replace carriage return
229 ptr = strchr(action, '\r');
230 if (ptr != NULL) {
231 *ptr = '\0';
232 }
233
234 if (strcmp(action, "load") == 0) {
235 ruleCfg->action = DEVMGR_ACTION_LOAD;
236 } else if (strcmp(action, "unload") == 0) {
237 ruleCfg->action = DEVMGR_ACTION_UNLOAD;
238 } else {
239 HDF_LOGE("parse event action fail [%{public}s]", action);
240 return HDF_FAILURE;
241 }
242
243 HDF_LOGD("event:%{public}s:%{public}d\n", event, ruleCfg->action);
244 ruleCfg->serviceName = strdup(event);
245 if (ruleCfg->serviceName == NULL) {
246 HDF_LOGE("ruleCfg->serviceName is NULL");
247 return HDF_FAILURE;
248 }
249 if (DListGetCount(&ruleCfg->matchKeyList) == 0) {
250 HDF_LOGE("parse failed, no match key");
251 return HDF_FAILURE;
252 }
253
254 return HDF_SUCCESS;
255 }
256
DevMgrUeventParseRule(char * line)257 static int32_t DevMgrUeventParseRule(char *line)
258 {
259 if (!DevMgrUeventCheckRuleValid(line)) {
260 return HDF_FAILURE;
261 }
262
263 struct DevMgrUeventRuleCfg *ruleCfg = (struct DevMgrUeventRuleCfg *)OsalMemCalloc(sizeof(*ruleCfg));
264 if (ruleCfg == NULL) {
265 HDF_LOGE("%{public}s OsalMemCalloc ruleCfg failed", __func__);
266 return HDF_FAILURE;
267 }
268 if (ruleCfg->serviceName == NULL) {
269 HDF_LOGW("%{public}s OsalMemCalloc ruleCfg->serviceName failed", __func__);
270 }
271 DListHeadInit(&ruleCfg->matchKeyList);
272
273 char *ptr = line;
274 char *subStr = line;
275 while (ptr != NULL && *ptr != '\0') {
276 ptr = strstr(ptr, KEY_DELIMITER);
277 if (ptr != NULL) {
278 *ptr = '\0';
279 if (DevMgrUeventParseMatchKey(subStr, &ruleCfg->matchKeyList) != HDF_SUCCESS) {
280 goto FAIL;
281 }
282 ptr += strlen(KEY_DELIMITER);
283 subStr = ptr;
284 } else {
285 if (DevMgrUeventParseHdfEvent(subStr, ruleCfg) != HDF_SUCCESS) {
286 goto FAIL;
287 }
288 DListInsertTail(&ruleCfg->entry, DevMgrUeventRuleCfgList());
289 }
290 }
291
292 return HDF_SUCCESS;
293
294 FAIL:
295 DevMgrUeventReleaseKeyList(&ruleCfg->matchKeyList);
296 OsalMemFree(ruleCfg->serviceName);
297 OsalMemFree(ruleCfg);
298 return HDF_FAILURE;
299 }
300
DevMgrUeventDisplayRuleCfgList(void)301 static void DevMgrUeventDisplayRuleCfgList(void)
302 {
303 struct DevMgrUeventRuleCfg *ruleCfg = NULL;
304 struct DevMgrMatchKey *matchKey = NULL;
305 struct DListHead *ruleCfgList = DevMgrUeventRuleCfgList();
306
307 DLIST_FOR_EACH_ENTRY(ruleCfg, ruleCfgList, struct DevMgrUeventRuleCfg, entry) {
308 HDF_LOGD("service:%{public}s action:%{public}d", ruleCfg->serviceName, ruleCfg->action);
309 DLIST_FOR_EACH_ENTRY(matchKey, &ruleCfg->matchKeyList, struct DevMgrMatchKey, entry) {
310 HDF_LOGD("key:%{public}s value:%{public}s", matchKey->key, matchKey->value);
311 }
312 }
313 }
314
DevMgrUeventParseConfig(void)315 static int32_t DevMgrUeventParseConfig(void)
316 {
317 char path[PATH_MAX] = {0};
318 int ret = sprintf_s(path, PATH_MAX - 1, "%s/hdf_pnp.cfg", HDF_CONFIG_DIR);
319 if (ret < 0) {
320 HDF_LOGE("%{public}s generate file path failed", __func__);
321 return HDF_FAILURE;
322 }
323
324 char resolvedPath[PATH_MAX] = {0};
325 if (realpath(path, resolvedPath) == NULL) {
326 HDF_LOGE("realpath file: %{public}s failed, errno:%{public}d", path, errno);
327 return HDF_FAILURE;
328 }
329 if (strncmp(resolvedPath, HDF_CONFIG_DIR, strlen(HDF_CONFIG_DIR)) != 0) {
330 HDF_LOGE("%{public}s invalid path %{public}s", __func__, resolvedPath);
331 return HDF_FAILURE;
332 }
333 FILE *file = fopen(resolvedPath, "r");
334 if (file == NULL) {
335 HDF_LOGE("%{public}s fopen %{public}s failed:%{public}d", __func__, resolvedPath, errno);
336 return HDF_FAILURE;
337 }
338
339 char line[LINE_MAX] = {0};
340 while (fgets(line, sizeof(line), file) != NULL) {
341 HDF_LOGD("%{public}s, %{public}s", __func__, line);
342 DevMgrUeventParseRule(line);
343 (void)memset_s(line, sizeof(line), 0, sizeof(line));
344 }
345
346 (void)fclose(file);
347 DevMgrUeventDisplayRuleCfgList();
348
349 return HDF_SUCCESS;
350 }
351
DevMgrUeventSocketInit(void)352 static int32_t DevMgrUeventSocketInit(void)
353 {
354 struct sockaddr_nl addr;
355 (void)memset_s(&addr, sizeof(addr), 0, sizeof(addr));
356 addr.nl_family = AF_NETLINK;
357 addr.nl_pid = (__u32)getpid();
358 addr.nl_groups = 0xffffffff;
359
360 int32_t sockfd = socket(PF_NETLINK, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, NETLINK_KOBJECT_UEVENT);
361 if (sockfd < 0) {
362 HDF_LOGE("create socket failed, err = %{public}d", errno);
363 return HDF_FAILURE;
364 }
365
366 int32_t buffSize = UEVENT_SOCKET_BUFF_SIZE;
367 if (setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, &buffSize, sizeof(buffSize)) != 0) {
368 HDF_LOGE("setsockopt: SO_RCVBUF failed err = %{public}d", errno);
369 close(sockfd);
370 return HDF_FAILURE;
371 }
372
373 const int32_t on = 1;
374 if (setsockopt(sockfd, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on)) != 0) {
375 HDF_LOGE("setsockopt: SO_PASSCRED failed, err = %{public}d", errno);
376 close(sockfd);
377 return HDF_FAILURE;
378 }
379
380 if (bind(sockfd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
381 HDF_LOGE("bind socket failed, err = %{public}d", errno);
382 close(sockfd);
383 return HDF_FAILURE;
384 }
385
386 return sockfd;
387 }
388
DevMgrReadUeventMessage(int sockFd,char * buffer,size_t length)389 static ssize_t DevMgrReadUeventMessage(int sockFd, char *buffer, size_t length)
390 {
391 struct sockaddr_nl addr;
392 (void)memset_s(&addr, sizeof(addr), 0, sizeof(addr));
393
394 struct iovec iov;
395 iov.iov_base = buffer;
396 iov.iov_len = length;
397
398 char credMsg[CMSG_SPACE(sizeof(struct ucred))] = {0};
399 struct msghdr msghdr = {0};
400 msghdr.msg_name = &addr;
401 msghdr.msg_namelen = sizeof(addr);
402 msghdr.msg_iov = &iov;
403 msghdr.msg_iovlen = 1;
404 msghdr.msg_control = credMsg;
405 msghdr.msg_controllen = sizeof(credMsg);
406
407 ssize_t n = recvmsg(sockFd, &msghdr, 0);
408 if (n <= 0) {
409 return HDF_FAILURE;
410 }
411
412 struct cmsghdr *hdr = CMSG_FIRSTHDR(&msghdr);
413 if (hdr == NULL || hdr->cmsg_type != SCM_CREDENTIALS) {
414 HDF_LOGI("Unexpected control message, ignored");
415 // drop this message
416 *buffer = '\0';
417 return HDF_FAILURE;
418 }
419
420 return n;
421 }
422
DevMgrUeventMsgCheckValid(const char * msg,ssize_t msgLen)423 static bool DevMgrUeventMsgCheckValid(const char *msg, ssize_t msgLen)
424 {
425 (void)msgLen;
426 if (strncmp(msg, "libudev", strlen("libudev")) == 0) {
427 return false;
428 }
429 return true;
430 }
431
DevMgrUeventMatchRule(struct DListHead * keyList,struct DListHead * ruleKeyList)432 static bool DevMgrUeventMatchRule(struct DListHead *keyList, struct DListHead *ruleKeyList)
433 {
434 struct DevMgrMatchKey *key = NULL;
435 struct DevMgrMatchKey *keyMsg = NULL;
436
437 DLIST_FOR_EACH_ENTRY(key, ruleKeyList, struct DevMgrMatchKey, entry) {
438 bool match = false;
439 DLIST_FOR_EACH_ENTRY(keyMsg, keyList, struct DevMgrMatchKey, entry) {
440 if (strcmp(key->key, keyMsg->key) == 0) {
441 if (strcmp(key->value, keyMsg->value) == 0) {
442 match = true;
443 break;
444 } else {
445 return false;
446 }
447 }
448 }
449 if (!match) {
450 return false;
451 }
452 }
453
454 return true;
455 }
456
DevMgrUeventProcessPnPEvent(const struct DevMgrUeventRuleCfg * ruleCfg)457 static void DevMgrUeventProcessPnPEvent(const struct DevMgrUeventRuleCfg *ruleCfg)
458 {
459 struct IDevmgrService *instance = DevmgrServiceGetInstance();
460
461 if (instance == NULL) {
462 HDF_LOGE("Getting DevmgrService instance failed");
463 return;
464 }
465
466 if (ruleCfg->action == DEVMGR_ACTION_LOAD) {
467 instance->LoadDevice(instance, ruleCfg->serviceName);
468 } else {
469 instance->UnloadDevice(instance, ruleCfg->serviceName);
470 }
471 }
472
DevMgrUeventMatchRules(struct DListHead * keyList,struct DListHead * ruleList)473 static bool DevMgrUeventMatchRules(struct DListHead *keyList, struct DListHead *ruleList)
474 {
475 struct DevMgrUeventRuleCfg *ruleCfg = NULL;
476
477 DLIST_FOR_EACH_ENTRY(ruleCfg, ruleList, struct DevMgrUeventRuleCfg, entry) {
478 if (DevMgrUeventMatchRule(keyList, &ruleCfg->matchKeyList)) {
479 HDF_LOGI("%{public}s %{public}s %{public}d", __func__, ruleCfg->serviceName, ruleCfg->action);
480 DevMgrUeventProcessPnPEvent(ruleCfg);
481 return true;
482 }
483 }
484
485 return false;
486 }
487
DevMgrParseUevent(char * msg,ssize_t msgLen)488 static int32_t DevMgrParseUevent(char *msg, ssize_t msgLen)
489 {
490 if (!DevMgrUeventMsgCheckValid(msg, msgLen)) {
491 return HDF_FAILURE;
492 }
493
494 struct DListHead keyList;
495 DListHeadInit(&keyList);
496 struct DevMgrMatchKey *key = NULL;
497
498 for (char *ptr = msg; ptr < (msg + msgLen);) {
499 if (*ptr == '0') {
500 ptr++;
501 continue;
502 }
503
504 if (key == NULL) {
505 key = (struct DevMgrMatchKey *)OsalMemCalloc(sizeof(*key));
506 if (key == NULL) {
507 DevMgrUeventReleaseKeyList(&keyList);
508 return HDF_FAILURE;
509 }
510 DListHeadInit(&key->entry);
511 }
512
513 uint32_t subStrLen = (uint32_t)strlen(ptr) + 1;
514 HDF_LOGD("%{public}s %{public}d [%{private}s]", __func__, subStrLen, ptr);
515 if (DevMgrUeventParseKeyValue(ptr, key, UEVENT_DELIMITER) == HDF_SUCCESS) {
516 DListInsertHead(&key->entry, &keyList);
517 key = NULL;
518 }
519 ptr += subStrLen;
520 }
521
522 HDF_LOGD("%{public}s {%{public}s} %{public}zd", __func__, msg, msgLen);
523 DevMgrUeventMatchRules(&keyList, DevMgrUeventRuleCfgList());
524 DevMgrUeventReleaseKeyList(&keyList);
525
526 return HDF_SUCCESS;
527 }
528
529 #define DEVMGR_UEVENT_MSG_SIZE 2048
530 #define DEVMGR_UEVENT_WAIT_TIME 1000
DevMgrUeventThread(void * arg)531 static int32_t DevMgrUeventThread(void *arg)
532 {
533 (void)arg;
534 int32_t sfd = DevMgrUeventSocketInit();
535 if (sfd < 0) {
536 HDF_LOGE("DevMgrUeventSocketInit get sfd error");
537 return HDF_FAILURE;
538 }
539
540 char msg[DEVMGR_UEVENT_MSG_SIZE + 1] = {0};
541 ssize_t msgLen;
542 struct pollfd fd;
543 (void)memset_s(&fd, sizeof(fd), 0, sizeof(fd));
544 fd.fd = sfd;
545 fd.events = POLLIN | POLLERR;
546 while (true) {
547 if (poll(&fd, 1, -1) <= 0) {
548 HDF_LOGE("%{public}s poll fail %{public}d", __func__, errno);
549 OsalMSleep(DEVMGR_UEVENT_WAIT_TIME);
550 continue;
551 }
552 if (((uint32_t)fd.revents & (POLLIN | POLLERR)) != 0) {
553 int backErrno = errno;
554 msgLen = DevMgrReadUeventMessage(sfd, msg, DEVMGR_UEVENT_MSG_SIZE);
555 if (((uint32_t)fd.revents & POLLERR) != 0) {
556 HDF_LOGE("%{public}s poll error", __func__);
557 }
558 if (msgLen <= 0) {
559 HDF_LOGE("%{public}s recv errno: %{public}d", __func__, backErrno);
560 continue;
561 }
562 DevMgrParseUevent(msg, msgLen);
563 (void)memset_s(&msg, sizeof(msg), 0, sizeof(msg));
564 }
565 }
566
567 DevMgrUeventReleaseRuleCfgList();
568 close(sfd);
569 return HDF_SUCCESS;
570 }
571
572 #define DEVMGR_UEVENT_STACK_SIZE 100000
573
DevMgrUeventReceiveStart(void)574 int32_t DevMgrUeventReceiveStart(void)
575 {
576 HDF_LOGI("DevMgrUeventReceiveStart");
577 if (DevMgrUeventParseConfig() == HDF_FAILURE) {
578 return HDF_FAILURE;
579 }
580
581 struct OsalThreadParam threadCfg;
582 (void)memset_s(&threadCfg, sizeof(threadCfg), 0, sizeof(threadCfg));
583 threadCfg.name = "DevMgrUevent";
584 threadCfg.priority = OSAL_THREAD_PRI_HIGH;
585 threadCfg.stackSize = DEVMGR_UEVENT_STACK_SIZE;
586
587 OSAL_DECLARE_THREAD(thread);
588 (void)OsalThreadCreate(&thread, (OsalThreadEntry)DevMgrUeventThread, NULL);
589 (void)OsalThreadStart(&thread, &threadCfg);
590
591 return HDF_SUCCESS;
592 }
593