1/* 2 * Copyright (C) 2024 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#include "host_usb.h" 16 17#include <thread> 18#include "usb_util.h" 19 20namespace Hdc { 21constexpr uint16_t DEVICE_CHECK_INTERVAL = 3000; // ms 22constexpr uint16_t BUF_SIZE_MEDIUM = 512; 23constexpr uint16_t BUF_SIZE_TINY = 64; 24constexpr uint8_t GLOBAL_TIMEOUT = 30; 25constexpr uint16_t TIME_BASE = 1000; 26constexpr uint16_t MAX_SIZE_IOBUF = 61440; 27 28uint8_t *g_bufPtr = nullptr; 29 30const std::string StringFormat(const char * const formater, va_list &vaArgs) 31{ 32 std::vector<char> args(MAX_SIZE_IOBUF); 33 const int retSize = vsnprintf_s(args.data(), MAX_SIZE_IOBUF, MAX_SIZE_IOBUF - 1, formater, vaArgs); 34 if (retSize < 0) { 35 return std::string(""); 36 } else { 37 return std::string(args.data(), retSize); 38 } 39} 40 41const std::string StringFormat(const char * const formater, ...) 42{ 43 va_list vaArgs; 44 va_start(vaArgs, formater); 45 std::string ret = StringFormat(formater, vaArgs); 46 va_end(vaArgs); 47 return ret; 48} 49 50HostUsb::HostUsb() 51{ 52 if (libusb_init((libusb_context **)&ctxUSB) != 0) { 53 ctxUSB = nullptr; 54 } 55 running = false; 56} 57 58HostUsb::~HostUsb() 59{ 60 if (running) { 61 Stop(); 62 } 63} 64 65void HostUsb::Stop() 66{ 67 if (!ctxUSB) { 68 return; 69 } 70 timer->Stop(); 71 libusb_exit((libusb_context *)ctxUSB); 72 running = false; 73 74 if (g_bufPtr != nullptr) { 75 delete[] g_bufPtr; 76 g_bufPtr = nullptr; 77 } 78} 79 80// Main thread USB operates in this thread 81void HostUsb::UsbWorkThread(void *arg) 82{ 83 HostUsb *thisClass = (HostUsb *)arg; 84 constexpr uint8_t usbHandleTimeout = 30; // second 85 while (thisClass->running) { 86 struct timeval zerotime; 87 zerotime.tv_sec = usbHandleTimeout; 88 zerotime.tv_usec = 0; // if == 0,windows will be high CPU load 89 libusb_handle_events_timeout(thisClass->ctxUSB, &zerotime); 90 } 91} 92 93void HostUsb::WatchUsbNodeChange(void *arg) 94{ 95 HostUsb *thisClass = (HostUsb *)arg; 96 libusb_device **devs = nullptr; 97 libusb_device *dev = nullptr; 98 ssize_t cnt = libusb_get_device_list(thisClass->ctxUSB, &devs); 99 if (cnt < 0) { 100 return; 101 } 102 int i = 0; 103 // linux replug devid increment,windows will be not 104 while ((dev = devs[i++]) != nullptr) { // must postfix++ 105 std::string szTmpKey = StringFormat("%d-%d", libusb_get_bus_number(dev), libusb_get_device_address(dev)); 106 // check is in ignore list 107 UsbCheckStatus statusCheck = thisClass->mapIgnoreDevice[szTmpKey]; 108 if (statusCheck == HOST_USB_IGNORE || statusCheck == HOST_USB_REGISTER) { 109 continue; 110 } 111 std::string sn = szTmpKey; 112 if (thisClass->HasValidDevice(dev) && !thisClass->DetectMyNeed(dev, sn)) { 113 thisClass->ReviewUsbNodeLater(szTmpKey); 114 } 115 } 116 libusb_free_device_list(devs, 1); 117} 118 119void HostUsb::ReviewUsbNodeLater(string &nodeKey) 120{ 121 // add to ignore list 122 mapIgnoreDevice[nodeKey] = HOST_USB_IGNORE; 123 RemoveIgnoreDevice(nodeKey); 124} 125 126bool HostUsb::HasValidDevice(libusb_device *device) 127{ 128 struct libusb_config_descriptor *descConfig = nullptr; 129 int ret = libusb_get_active_config_descriptor(device, &descConfig); 130 if (ret != 0) { 131 return false; 132 } 133 bool hasValid = false; 134 for (unsigned int j = 0; j < descConfig->bNumInterfaces; ++j) { 135 const struct libusb_interface *interface = &descConfig->interface[j]; 136 if (interface->num_altsetting < 1) { 137 continue; 138 } 139 const struct libusb_interface_descriptor *ifDescriptor = &interface->altsetting[0]; 140 if (!IsDebuggableDev(ifDescriptor)) { 141 continue; 142 } 143 hasValid = true; 144 break; 145 } 146 return hasValid; 147} 148 149bool HostUsb::IsDebuggableDev(const struct libusb_interface_descriptor *ifDescriptor) 150{ 151 constexpr uint8_t harmonyEpNum = 2; 152 constexpr uint8_t harmonyClass = 0xff; 153 constexpr uint8_t harmonySubClass = 0x50; 154 constexpr uint8_t harmonyProtocol = 0x01; 155 156 if (ifDescriptor->bInterfaceClass != harmonyClass || ifDescriptor->bInterfaceSubClass != harmonySubClass || 157 ifDescriptor->bInterfaceProtocol != harmonyProtocol) { 158 return false; 159 } 160 if (ifDescriptor->bNumEndpoints != harmonyEpNum) { 161 return false; 162 } 163 return true; 164} 165 166bool HostUsb::DetectMyNeed(libusb_device *device, string &sn) 167{ 168 HUSB hUSB = new(std::nothrow) HdcUSB(); 169 if (hUSB == nullptr) { 170 return false; 171 } 172 hUSB->device = device; 173 // just get usb SN, close handle immediately 174 int childRet = OpenDeviceMyNeed(hUSB); 175 if (childRet < 0) { 176 delete hUSB; 177 return false; 178 } 179 UpdateUSBDaemonInfo(hUSB, STATUS_READY); 180 mapIgnoreDevice[sn] = HOST_USB_REGISTER; 181 mapUsbDevice[hUSB->serialNumber] = hUSB; 182 return true; 183} 184 185void HostUsb::UpdateUSBDaemonInfo(HUSB hUSB, uint8_t connStatus) 186{ 187 // add to list 188 HdcDaemonInformation di; 189 di.connectKey = hUSB->serialNumber; 190 di.connType = CONN_USB; 191 di.connStatus = connStatus; 192 di.usbMountPoint = ""; 193 di.usbMountPoint = StringFormat("%d-%d", hUSB->busId, hUSB->devId); 194 195 HDaemonInfo pDi = nullptr; 196 HDaemonInfo hdiNew = &di; 197 AdminDaemonMap(OP_QUERY, hUSB->serialNumber, pDi); 198 if (!pDi) { 199 AdminDaemonMap(OP_ADD, hUSB->serialNumber, hdiNew); 200 } else { 201 AdminDaemonMap(OP_UPDATE, hUSB->serialNumber, hdiNew); 202 if (connStatus == STATUS_OFFLINE) { 203 RemoveIgnoreDevice(di.usbMountPoint); 204 } 205 } 206} 207 208// ==0 Represents new equipment and is what we need,<0 my need 209int HostUsb::OpenDeviceMyNeed(HUSB hUSB) 210{ 211 libusb_device *device = hUSB->device; 212 int ret = -1; 213 int openRet = libusb_open(device, &hUSB->devHandle); 214 if (openRet != LIBUSB_SUCCESS) { 215 return -1; 216 } 217 while (running) { 218 libusb_device_handle *handle = hUSB->devHandle; 219 struct libusb_device_descriptor desc; 220 if (CheckDescriptor(hUSB, desc)) { 221 break; 222 } 223#ifdef HOST_MAC 224 if (CheckActiveConfig(device, hUSB, desc)) { 225#else 226 if (CheckActiveConfig(device, hUSB)) { 227#endif 228 break; 229 } 230 231 // USB filter rules are set according to specific device pedding device 232 ret = libusb_claim_interface(handle, hUSB->interfaceNumber); 233 break; 234 } 235 if (ret) { 236 // not my need device, release the device 237 libusb_close(hUSB->devHandle); 238 hUSB->devHandle = nullptr; 239 } 240 return ret; 241} 242 243int HostUsb::CheckDescriptor(HUSB hUSB, libusb_device_descriptor& desc) 244{ 245 char serialNum[BUF_SIZE_MEDIUM] = ""; 246 int childRet = 0; 247 uint8_t curBus = libusb_get_bus_number(hUSB->device); 248 uint8_t curDev = libusb_get_device_address(hUSB->device); 249 hUSB->busId = curBus; 250 hUSB->devId = curDev; 251 if (libusb_get_device_descriptor(hUSB->device, &desc)) { 252 return -1; 253 } 254 // Get the serial number of the device, if there is no serial number, use the ID number to replace 255 // If the device is not in time, occasionally can't get it, this is determined by the external factor, cannot be 256 // changed. LIBUSB_SUCCESS 257 childRet = libusb_get_string_descriptor_ascii(hUSB->devHandle, desc.iSerialNumber, (uint8_t *)serialNum, 258 sizeof(serialNum)); 259 if (childRet < 0) { 260 return -1; 261 } else { 262 hUSB->serialNumber = serialNum; 263 } 264 return 0; 265} 266 267#ifdef HOST_MAC 268int HostUsb::CheckActiveConfig(libusb_device *device, HUSB hUSB, libusb_device_descriptor& desc) 269#else 270int HostUsb::CheckActiveConfig(libusb_device *device, HUSB hUSB) 271#endif 272{ 273 struct libusb_config_descriptor *descConfig = nullptr; 274 int ret = libusb_get_active_config_descriptor(device, &descConfig); 275 if (ret != 0) { 276#ifdef HOST_MAC 277 if ((desc.bDeviceClass == 0xFF) 278 && (desc.bDeviceSubClass == 0xFF) 279 && (desc.bDeviceProtocol == 0xFF)) { 280 ret = libusb_set_configuration(hUSB->devHandle, 1); 281 if (ret != 0) { 282 return -1; 283 } 284 } 285 286 ret = libusb_get_active_config_descriptor(device, &descConfig); 287 if (ret != 0) { 288#endif 289 return -1; 290 } 291#ifdef HOST_MAC 292 } 293#endif 294 295 ret = -1; 296 CheckUsbEndpoint(ret, hUSB, descConfig); 297 libusb_free_config_descriptor(descConfig); 298 return ret; 299} 300 301void HostUsb::CheckUsbEndpoint(int& ret, HUSB hUSB, libusb_config_descriptor *descConfig) 302{ 303 unsigned int j = 0; 304 for (j = 0; j < descConfig->bNumInterfaces; ++j) { 305 const struct libusb_interface *interface = &descConfig->interface[j]; 306 if (interface->num_altsetting < 1) { 307 continue; 308 } 309 const struct libusb_interface_descriptor *ifDescriptor = &interface->altsetting[0]; 310 if (!IsDebuggableDev(ifDescriptor)) { 311 continue; 312 } 313 hUSB->interfaceNumber = ifDescriptor->bInterfaceNumber; 314 unsigned int k = 0; 315 for (k = 0; k < ifDescriptor->bNumEndpoints; ++k) { 316 const struct libusb_endpoint_descriptor *ep_desc = &ifDescriptor->endpoint[k]; 317 if ((ep_desc->bmAttributes & 0x03) != LIBUSB_TRANSFER_TYPE_BULK) { 318 continue; 319 } 320 if (ep_desc->bEndpointAddress & LIBUSB_ENDPOINT_IN) { 321 hUSB->hostBulkIn.endpoint = ep_desc->bEndpointAddress; 322 hUSB->hostBulkIn.bulkInOut = true; 323 } else { 324 hUSB->hostBulkOut.endpoint = ep_desc->bEndpointAddress; 325 hUSB->wMaxPacketSizeSend = ep_desc->wMaxPacketSize; 326 hUSB->hostBulkOut.bulkInOut = false; 327 } 328 } 329 if (hUSB->hostBulkIn.endpoint == 0 || hUSB->hostBulkOut.endpoint == 0) { 330 break; 331 } 332 ret = 0; 333 } 334} 335 336bool HostUsb::FindDeviceByID(HUSB hUSB, const char *usbMountPoint, libusb_context *ctxUSB) 337{ 338 libusb_device **listDevices = nullptr; 339 bool ret = false; 340 char tmpStr[BUF_SIZE_TINY] = ""; 341 int busNum = 0; 342 int devNum = 0; 343 int curBus = 0; 344 int curDev = 0; 345 346 int deviceNum = libusb_get_device_list(ctxUSB, &listDevices); 347 if (deviceNum <= 0) { 348 libusb_free_device_list(listDevices, 1); 349 return false; 350 } 351 if (strchr(usbMountPoint, '-') && EOK == strcpy_s(tmpStr, sizeof(tmpStr), usbMountPoint)) { 352 *strchr(tmpStr, '-') = '\0'; 353 busNum = atoi(tmpStr); 354 devNum = atoi(tmpStr + strlen(tmpStr) + 1); 355 } else { 356 return false; 357 } 358 359 int i = 0; 360 for (i = 0; i < deviceNum; ++i) { 361 struct libusb_device_descriptor desc; 362 if (LIBUSB_SUCCESS != libusb_get_device_descriptor(listDevices[i], &desc)) { 363 continue; 364 } 365 curBus = libusb_get_bus_number(listDevices[i]); 366 curDev = libusb_get_device_address(listDevices[i]); 367 if ((curBus == busNum && curDev == devNum)) { 368 hUSB->device = listDevices[i]; 369 int childRet = OpenDeviceMyNeed(hUSB); 370 if (!childRet) { 371 ret = true; 372 } else { 373 string key = string(usbMountPoint); 374 RemoveIgnoreDevice(key); 375 } 376 break; 377 } 378 } 379 libusb_free_device_list(listDevices, 1); 380 return ret; 381} 382 383// multi-thread calll 384void HostUsb::CancelUsbIo(HUSB hUSB) 385{ 386 std::unique_lock<std::mutex> lock(hUSB->lockDeviceHandle); 387 if (!hUSB->hostBulkIn.isShutdown) { 388 if (!hUSB->hostBulkIn.isComplete) { 389 libusb_cancel_transfer(hUSB->hostBulkIn.transfer); 390 hUSB->hostBulkIn.cv.notify_one(); 391 } else { 392 hUSB->hostBulkIn.isShutdown = true; 393 } 394 } 395 if (!hUSB->hostBulkOut.isShutdown) { 396 if (!hUSB->hostBulkOut.isComplete) { 397 libusb_cancel_transfer(hUSB->hostBulkOut.transfer); 398 hUSB->hostBulkOut.cv.notify_one(); 399 } else { 400 hUSB->hostBulkOut.isShutdown = true; 401 } 402 } 403} 404 405void HostUsb::RemoveIgnoreDevice(string &mountInfo) 406{ 407 if (mapIgnoreDevice.count(mountInfo)) { 408 mapIgnoreDevice.erase(mountInfo); 409 } 410} 411 412void LIBUSB_CALL HostUsb::USBBulkCallback(struct libusb_transfer *transfer) 413{ 414 auto *ep = reinterpret_cast<HostUSBEndpoint *>(transfer->user_data); 415 std::unique_lock<std::mutex> lock(ep->mutexIo); 416 bool retrySumit = false; 417 int childRet = 0; 418 do { 419 if (transfer->status != LIBUSB_TRANSFER_COMPLETED) { 420 break; 421 } 422 if (!ep->bulkInOut && transfer->actual_length != transfer->length) { 423 transfer->length -= transfer->actual_length; 424 transfer->buffer += transfer->actual_length; 425 retrySumit = true; 426 break; 427 } 428 } while (false); 429 while (retrySumit) { 430 childRet = libusb_submit_transfer(transfer); 431 if (childRet != 0) { 432 transfer->status = LIBUSB_TRANSFER_ERROR; 433 break; 434 } 435 return; 436 } 437 ep->isComplete = true; 438 ep->cv.notify_one(); 439} 440 441PersistBuffer HostUsb::ReadUsbIO(HUSB hUSB, int exceptedSize) 442{ 443 int timeout = 0; 444 int childRet = 0; 445 int ret = 0; 446 447 HostUSBEndpoint* ep = &hUSB->hostBulkIn; 448 449 if (g_bufPtr == nullptr) { 450 g_bufPtr = new uint8_t[MAX_SIZE_IOBUF]; 451 } 452 453 hUSB->lockDeviceHandle.lock(); 454 ep->isComplete = false; 455 do { 456 std::unique_lock<std::mutex> lock(ep->mutexIo); 457 libusb_fill_bulk_transfer(ep->transfer, hUSB->devHandle, ep->endpoint, g_bufPtr, exceptedSize, 458 USBBulkCallback, ep, timeout); 459 childRet = libusb_submit_transfer(ep->transfer); 460 hUSB->lockDeviceHandle.unlock(); 461 if (childRet < 0) { 462 break; 463 } 464 ep->cv.wait(lock, [ep]() { return ep->isComplete; }); 465 if (ep->transfer->status != 0) { 466 break; 467 } 468 ret = ep->transfer->actual_length; 469 } while (false); 470 return PersistBuffer{reinterpret_cast<char *>(g_bufPtr), static_cast<uint64_t>(ret)}; 471} 472 473HUSB HostUsb::GetUsbDevice(std::string connectKey) 474{ 475 return mapUsbDevice[connectKey]; 476} 477 478int HostUsb::WriteUsbIO(HUSB hUSB, SerializedBuffer buf) 479{ 480 int childRet = 0; 481 int ret = -14000; 482 int timeout = GLOBAL_TIMEOUT * TIME_BASE; 483 HostUSBEndpoint *ep = &hUSB->hostBulkOut; 484 485 hUSB->lockDeviceHandle.lock(); 486 ep->isComplete = false; 487 uint8_t* ptr = reinterpret_cast<uint8_t *>(buf.ptr); 488 size_t size = static_cast<size_t>(buf.size); 489 do { 490 std::unique_lock<std::mutex> lock(ep->mutexIo); 491 libusb_fill_bulk_transfer(ep->transfer, hUSB->devHandle, ep->endpoint, ptr, size, USBBulkCallback, ep, 492 timeout); 493 childRet = libusb_submit_transfer(ep->transfer); 494 hUSB->lockDeviceHandle.unlock(); 495 if (childRet < 0) { 496 break; 497 } 498 ep->cv.wait(lock, [ep]() { return ep->isComplete; }); 499 if (ep->transfer->status != 0) { 500 break; 501 } 502 ret = ep->transfer->actual_length; 503 } while (false); 504 return ret; 505} 506 507int HostUsb::Initial() 508{ 509 if (!ctxUSB) { 510 return -1; 511 } 512 running = true; 513 auto WatchUsbNodeChangeFunc = [this]() { WatchUsbNodeChange(this); }; 514 timer = std::make_unique<CTimer>(WatchUsbNodeChangeFunc); 515 timer->Start(DEVICE_CHECK_INTERVAL); 516 std::thread([this]() { 517 UsbWorkThread(this); 518 }).detach(); 519 return 0; 520} 521 522static void BuildDaemonVisableLine(HDaemonInfo hdi, bool fullDisplay, string &out) 523{ 524 if (fullDisplay) { 525 string sConn; 526 string sStatus; 527 switch (hdi->connType) { 528 case CONN_TCP: 529 sConn = "TCP"; 530 break; 531 case CONN_USB: 532 sConn = "USB"; 533 break; 534#ifdef HDC_SUPPORT_UART 535 case CONN_SERIAL: 536 sConn = "UART"; 537 break; 538#endif 539 case CONN_BT: 540 sConn = "BT"; 541 break; 542 default: 543 sConn = "UNKNOW"; 544 break; 545 } 546 switch (hdi->connStatus) { 547 case STATUS_READY: 548 sStatus = "Ready"; 549 break; 550 case STATUS_CONNECTED: 551 sStatus = "Connected"; 552 break; 553 case STATUS_OFFLINE: 554 sStatus = "Offline"; 555 break; 556 default: 557 sStatus = "UNKNOW"; 558 break; 559 } 560 out = StringFormat("%s\t\t%s\t%s\t%s\n", hdi->connectKey.c_str(), sConn.c_str(), sStatus.c_str(), 561 hdi->devName.c_str()); 562 } else { 563 if (hdi->connStatus == STATUS_CONNECTED) { 564 out = StringFormat("%s\n", hdi->connectKey.c_str()); 565 } 566 } 567} 568 569string HostUsb::GetDaemonMapList(uint8_t opType) 570{ 571 string ret; 572 bool fullDisplay = false; 573 if (opType == OP_GET_STRLIST_FULL) { 574 fullDisplay = true; 575 } 576 lockMapDaemon.lock(); 577 map<string, HDaemonInfo>::iterator iter; 578 string echoLine; 579 for (iter = mapDaemon.begin(); iter != mapDaemon.end(); ++iter) { 580 HDaemonInfo di = iter->second; 581 if (!di) { 582 continue; 583 } 584 echoLine = ""; 585 if (opType == OP_GET_READY_STRLIST) { 586 if (di->connStatus == STATUS_READY) { 587 echoLine = StringFormat("%s ", di->connectKey.c_str()); 588 ret += echoLine; 589 } 590 continue; 591 } 592 BuildDaemonVisableLine(di, fullDisplay, echoLine); 593 ret += echoLine; 594 } 595 lockMapDaemon.unlock(); 596 return ret; 597} 598 599string HostUsb::AdminDaemonMap(uint8_t opType, const string &connectKey, HDaemonInfo &hDaemonInfoInOut) 600{ 601 string sRet; 602 switch (opType) { 603 case OP_ADD: { 604 HDaemonInfo pdiNew = new(std::nothrow) HdcDaemonInformation(); 605 if (pdiNew == nullptr) { 606 break; 607 } 608 *pdiNew = *hDaemonInfoInOut; 609 lockMapDaemon.lock(); 610 if (!mapDaemon[hDaemonInfoInOut->connectKey]) { 611 mapDaemon[hDaemonInfoInOut->connectKey] = pdiNew; 612 } 613 lockMapDaemon.unlock(); 614 break; 615 } 616 case OP_GET_READY_STRLIST: 617 sRet = GetDaemonMapList(opType); 618 break; 619 case OP_GET_STRLIST: 620 case OP_GET_STRLIST_FULL: { 621 sRet = GetDaemonMapList(opType); 622 break; 623 } 624 case OP_QUERY: { 625 lockMapDaemon.lock(); 626 if (mapDaemon.count(connectKey)) { 627 hDaemonInfoInOut = mapDaemon[connectKey]; 628 } 629 lockMapDaemon.unlock(); 630 break; 631 } 632 case OP_REMOVE: { 633 lockMapDaemon.lock(); 634 if (mapDaemon.count(connectKey)) { 635 mapDaemon.erase(connectKey); 636 } 637 lockMapDaemon.unlock(); 638 break; 639 } 640 case OP_GET_ANY: { 641 lockMapDaemon.lock(); 642 map<string, HDaemonInfo>::iterator iter; 643 for (iter = mapDaemon.begin(); iter != mapDaemon.end(); ++iter) { 644 HDaemonInfo di = iter->second; 645 // usb will be auto connected 646 if (di->connStatus == STATUS_READY || di->connStatus == STATUS_CONNECTED) { 647 hDaemonInfoInOut = di; 648 break; 649 } 650 } 651 lockMapDaemon.unlock(); 652 break; 653 } 654 case OP_WAIT_FOR_ANY: { 655 lockMapDaemon.lock(); 656 map<string, HDaemonInfo>::iterator iter; 657 for (iter = mapDaemon.begin(); iter != mapDaemon.end(); ++iter) { 658 HDaemonInfo di = iter->second; 659 if (di->connStatus == STATUS_CONNECTED) { 660 hDaemonInfoInOut = di; 661 break; 662 } 663 } 664 lockMapDaemon.unlock(); 665 break; 666 } 667 case OP_UPDATE: { // Cannot update the Object HDi lower key value by direct value 668 lockMapDaemon.lock(); 669 HDaemonInfo hdi = mapDaemon[hDaemonInfoInOut->connectKey]; 670 if (hdi) { 671 *mapDaemon[hDaemonInfoInOut->connectKey] = *hDaemonInfoInOut; 672 } 673 lockMapDaemon.unlock(); 674 break; 675 } 676 default: 677 break; 678 } 679 return sRet; 680} 681}