1/** @addtogroup MCD_MCDIMPL_DAEMON_DEV 2 * @{ 3 * @file 4 * 5 * 6 * <!-- Copyright Giesecke & Devrient GmbH 2009 - 2012 --> 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 3. The name of the author may not be used to endorse or promote 17 * products derived from this software without specific prior 18 * written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS 21 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 22 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY 24 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE 26 * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 27 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 28 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 29 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 30 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 */ 32 33#include <cstdlib> 34#include <pthread.h> 35#include "McTypes.h" 36 37#include "DeviceScheduler.h" 38#include "DeviceIrqHandler.h" 39#include "ExcDevice.h" 40#include "Connection.h" 41#include "TrustletSession.h" 42 43#include "MobiCoreDevice.h" 44#include "Mci/mci.h" 45#include "mcLoadFormat.h" 46 47 48#include "log.h" 49#include "public/MobiCoreDevice.h" 50 51 52//------------------------------------------------------------------------------ 53MobiCoreDevice::MobiCoreDevice() 54{ 55 mcFault = false; 56} 57 58//------------------------------------------------------------------------------ 59MobiCoreDevice::~MobiCoreDevice() 60{ 61 delete mcVersionInfo; 62 mcVersionInfo = NULL; 63} 64 65//------------------------------------------------------------------------------ 66TrustletSession *MobiCoreDevice::getTrustletSession(uint32_t sessionId) 67{ 68 for (trustletSessionIterator_t session = trustletSessions.begin(); 69 session != trustletSessions.end(); 70 ++session) { 71 TrustletSession *tsTmp = *session; 72 if (tsTmp->sessionId == sessionId) { 73 return tsTmp; 74 } 75 } 76 return NULL; 77} 78 79 80void MobiCoreDevice::cleanSessionBuffers(TrustletSession *session) 81{ 82 CWsm_ptr pWsm = session->popBulkBuff(); 83 84 while (pWsm) { 85 unlockWsmL2(pWsm->handle); 86 pWsm = session->popBulkBuff(); 87 } 88} 89//------------------------------------------------------------------------------ 90void MobiCoreDevice::removeTrustletSession(uint32_t sessionId) 91{ 92 for (trustletSessionIterator_t session = trustletSessions.begin(); 93 session != trustletSessions.end(); 94 ++session) { 95 if ((*session)->sessionId == sessionId) { 96 cleanSessionBuffers(*session); 97 trustletSessions.erase(session); 98 return; 99 } 100 } 101} 102//------------------------------------------------------------------------------ 103Connection *MobiCoreDevice::getSessionConnection(uint32_t sessionId, notification_t *notification) 104{ 105 Connection *con = NULL; 106 TrustletSession *ts = NULL; 107 108 ts = getTrustletSession(sessionId); 109 if (ts == NULL) { 110 return NULL; 111 } 112 113 con = ts->notificationConnection; 114 if (con == NULL) { 115 ts->queueNotification(notification); 116 return NULL; 117 } 118 119 return con; 120} 121 122 123//------------------------------------------------------------------------------ 124bool MobiCoreDevice::open(Connection *connection) 125{ 126 // Link this device to the connection 127 connection->connectionData = this; 128 return true; 129} 130 131 132//------------------------------------------------------------------------------ 133/** 134 * Close device. 135 * 136 * Removes all sessions to a connection. Though, clientLib rejects the closeDevice() 137 * command if still sessions connected to the device, this is needed to clean up all 138 * sessions if client dies. 139 */ 140void MobiCoreDevice::close(Connection *connection) 141{ 142 trustletSessionList_t::reverse_iterator interator; 143 static CMutex mutex; 144 // 1. Iterate through device session to find connection 145 // 2. Decide what to do with open Trustlet sessions 146 // 3. Remove & delete deviceSession from vector 147 148 // Enter critical section 149 mutex.lock(); 150 for (interator = trustletSessions.rbegin(); 151 interator != trustletSessions.rend(); 152 interator++) { 153 TrustletSession *ts = *interator; 154 155 if (ts->deviceConnection == connection) { 156 closeSession(connection, ts->sessionId); 157 } 158 } 159 // Leave critical section 160 mutex.unlock(); 161 162 // After the trustlet is done make sure to tell the driver to cleanup 163 // all the orphaned drivers 164 cleanupWsmL2(); 165 166 connection->connectionData = NULL; 167} 168 169 170//------------------------------------------------------------------------------ 171void MobiCoreDevice::start(void) 172{ 173 // Call the device specific initialization 174 // initDevice(); 175 176 LOG_I("Starting DeviceIrqHandler..."); 177 // Start the irq handling thread 178 DeviceIrqHandler::start(); 179 180 if (schedulerAvailable()) { 181 LOG_I("Starting DeviceScheduler..."); 182 // Start the scheduling handling thread 183 DeviceScheduler::start(); 184 } else { 185 LOG_I("No DeviceScheduler available."); 186 } 187} 188 189 190//------------------------------------------------------------------------------ 191void MobiCoreDevice::signalMcpNotification(void) 192{ 193 mcpSessionNotification.signal(); 194} 195 196 197//------------------------------------------------------------------------------ 198bool MobiCoreDevice::waitMcpNotification(void) 199{ 200 int counter = 5; 201 while (1) { 202 // In case of fault just return, nothing to do here 203 if (mcFault) { 204 return false; 205 } 206 // Wait 10 seconds for notification 207 if (mcpSessionNotification.wait(10) == false) { 208 // No MCP answer received and mobicore halted, dump mobicore status 209 // then throw exception 210 LOG_I("No MCP answer received in 2 seconds."); 211 if (getMobicoreStatus() == MC_STATUS_HALT) { 212 dumpMobicoreStatus(); 213 mcFault = true; 214 return false; 215 } else { 216 counter--; 217 if (counter < 1) { 218 mcFault = true; 219 return false; 220 } 221 } 222 } else { 223 break; 224 } 225 } 226 227 // Check healthiness state of the device 228 if (DeviceIrqHandler::isExiting()) { 229 LOG_I("waitMcpNotification(): IrqHandler thread died! Joining"); 230 DeviceIrqHandler::join(); 231 LOG_I("waitMcpNotification(): Joined"); 232 LOG_E("IrqHandler thread died!"); 233 return false; 234 } 235 236 if (DeviceScheduler::isExiting()) { 237 LOG_I("waitMcpNotification(): Scheduler thread died! Joining"); 238 DeviceScheduler::join(); 239 LOG_I("waitMcpNotification(): Joined"); 240 LOG_E("Scheduler thread died!"); 241 return false; 242 } 243 return true; 244} 245 246 247//------------------------------------------------------------------------------ 248mcResult_t MobiCoreDevice::openSession( 249 Connection *deviceConnection, 250 loadDataOpenSession_ptr pLoadDataOpenSession, 251 MC_DRV_CMD_OPEN_SESSION_struct *cmdOpenSession, 252 mcDrvRspOpenSessionPayload_ptr pRspOpenSessionPayload 253) 254{ 255 do { 256 addr_t tci; 257 uint32_t len; 258 uint32_t handle = cmdOpenSession->handle; 259 260 if (!findContiguousWsm(handle, &tci, &len)) { 261 LOG_E("Failed to find contiguous WSM %u", handle); 262 return MC_DRV_ERR_DAEMON_WSM_HANDLE_NOT_FOUND; 263 } 264 265 if (!lockWsmL2(handle)) { 266 LOG_E("Failed to lock contiguous WSM %u", handle); 267 return MC_DRV_ERR_DAEMON_WSM_HANDLE_NOT_FOUND; 268 } 269 270 // Write MCP open message to buffer 271 mcpMessage->cmdOpen.cmdHeader.cmdId = MC_MCP_CMD_OPEN_SESSION; 272 mcpMessage->cmdOpen.uuid = cmdOpenSession->uuid; 273 mcpMessage->cmdOpen.wsmTypeTci = WSM_CONTIGUOUS; 274 mcpMessage->cmdOpen.adrTciBuffer = (uint32_t)(tci); 275 mcpMessage->cmdOpen.ofsTciBuffer = 0; 276 mcpMessage->cmdOpen.lenTciBuffer = len; 277 278 LOG_I(" Using phys=%p, len=%d as TCI buffer", 279 (addr_t)(cmdOpenSession->tci), 280 cmdOpenSession->len); 281 282 // check if load data is provided 283 mcpMessage->cmdOpen.wsmTypeLoadData = WSM_L2; 284 mcpMessage->cmdOpen.adrLoadData = (uint32_t)pLoadDataOpenSession->baseAddr; 285 mcpMessage->cmdOpen.ofsLoadData = pLoadDataOpenSession->offs; 286 mcpMessage->cmdOpen.lenLoadData = pLoadDataOpenSession->len; 287 memcpy(&mcpMessage->cmdOpen.tlHeader, pLoadDataOpenSession->tlHeader, sizeof(*pLoadDataOpenSession->tlHeader)); 288 289 // Clear the notifications queue. We asume the race condition we have 290 // seen in openSession never happens elsewhere 291 notifications = std::queue<notification_t>(); 292 // Notify MC about a new command inside the MCP buffer 293 notify(SID_MCP); 294 295 // Wait till response from MC is available 296 if (!waitMcpNotification()) { 297 // Here Mobicore can be considered dead. 298 unlockWsmL2(handle); 299 return MC_DRV_ERR_DAEMON_MCI_ERROR; 300 } 301 302 // Check if the command response ID is correct 303 if ((MC_MCP_CMD_OPEN_SESSION | FLAG_RESPONSE) != mcpMessage->rspHeader.rspId) { 304 LOG_E("CMD_OPEN_SESSION got invalid MCP command response(0x%X)", mcpMessage->rspHeader.rspId); 305 // Something is messing with our MCI memory, we cannot know if the Trustlet was loaded. 306 // Had in been loaded, we are loosing track of it here. 307 unlockWsmL2(handle); 308 return MC_DRV_ERR_DAEMON_MCI_ERROR; 309 } 310 311 uint32_t mcRet = mcpMessage->rspOpen.rspHeader.result; 312 313 if (mcRet != MC_MCP_RET_OK) { 314 LOG_E("MCP OPEN returned code %d.", mcRet); 315 unlockWsmL2(handle); 316 return MAKE_MC_DRV_MCP_ERROR(mcRet); 317 } 318 319 LOG_I(" After MCP OPEN, we have %d queued notifications", 320 notifications.size()); 321 // Read MC answer from MCP buffer 322 TrustletSession *trustletSession = new TrustletSession( 323 deviceConnection, 324 mcpMessage->rspOpen.sessionId); 325 326 pRspOpenSessionPayload->sessionId = trustletSession->sessionId; 327 pRspOpenSessionPayload->deviceSessionId = (uint32_t)trustletSession; 328 pRspOpenSessionPayload->sessionMagic = trustletSession->sessionMagic; 329 330 trustletSessions.push_back(trustletSession); 331 332 trustletSession->addBulkBuff(new CWsm((void *)pLoadDataOpenSession->offs, pLoadDataOpenSession->len, handle, 0)); 333 334 // We have some queued notifications and we need to send them to them 335 // trustlet session 336 while (!notifications.empty()) { 337 trustletSession->queueNotification(¬ifications.front()); 338 notifications.pop(); 339 } 340 341 } while (0); 342 return MC_DRV_OK; 343} 344 345 346//------------------------------------------------------------------------------ 347TrustletSession *MobiCoreDevice::registerTrustletConnection( 348 Connection *connection, 349 MC_DRV_CMD_NQ_CONNECT_struct *cmdNqConnect 350) 351{ 352 LOG_I(" Registering notification socket with Service session %d.", 353 cmdNqConnect->sessionId); 354 LOG_V(" Searching sessionId %d with sessionMagic %d", 355 cmdNqConnect->sessionId, 356 cmdNqConnect->sessionMagic); 357 358 for (trustletSessionIterator_t iterator = trustletSessions.begin(); 359 iterator != trustletSessions.end(); 360 ++iterator) { 361 TrustletSession *ts = *iterator; 362 363 if (ts != (TrustletSession *) (cmdNqConnect->deviceSessionId)) { 364 continue; 365 } 366 367 if ( (ts->sessionMagic != cmdNqConnect->sessionMagic) 368 || (ts->sessionId != cmdNqConnect->sessionId)) { 369 continue; 370 } 371 372 ts->notificationConnection = connection; 373 374 LOG_I(" Found Service session, registered connection."); 375 376 return ts; 377 } 378 379 LOG_I("registerTrustletConnection(): search failed"); 380 return NULL; 381} 382 383 384//------------------------------------------------------------------------------ 385mcResult_t MobiCoreDevice::closeSession(uint32_t sessionId) 386{ 387 LOG_I(" Write MCP CLOSE message to MCI, notify and wait"); 388 389 // Write MCP close message to buffer 390 mcpMessage->cmdClose.cmdHeader.cmdId = MC_MCP_CMD_CLOSE_SESSION; 391 mcpMessage->cmdClose.sessionId = sessionId; 392 393 // Notify MC about the availability of a new command inside the MCP buffer 394 notify(SID_MCP); 395 396 // Wait till response from MSH is available 397 if (!waitMcpNotification()) { 398 return MC_DRV_ERR_DAEMON_MCI_ERROR; 399 } 400 401 // Check if the command response ID is correct 402 if ((MC_MCP_CMD_CLOSE_SESSION | FLAG_RESPONSE) != mcpMessage->rspHeader.rspId) { 403 LOG_E("CMD_CLOSE_SESSION got invalid MCP response"); 404 return MC_DRV_ERR_DAEMON_MCI_ERROR; 405 } 406 407 // Read MC answer from MCP buffer 408 uint32_t mcRet = mcpMessage->rspOpen.rspHeader.result; 409 410 if (mcRet != MC_MCP_RET_OK) { 411 LOG_E("CMD_CLOSE_SESSION error %d", mcRet); 412 return MAKE_MC_DRV_MCP_ERROR(mcRet); 413 } 414 415 return MC_DRV_OK; 416} 417 418//------------------------------------------------------------------------------ 419/** 420 * TODO-2012-09-19-haenellu: Do some more checks here, otherwise rogue clientLib 421 * can close sessions from different TLCs. That is, deviceConnection is ignored below. 422 * 423 * Need connection as well as according session ID, so that a client can not 424 * close sessions not belonging to him. 425 */ 426mcResult_t MobiCoreDevice::closeSession(Connection *deviceConnection, uint32_t sessionId) 427{ 428 TrustletSession *ts = getTrustletSession(sessionId); 429 if (ts == NULL) { 430 LOG_E("no session found with id=%d", sessionId); 431 return MC_DRV_ERR_DAEMON_UNKNOWN_SESSION; 432 } 433 434 uint32_t mcRet = closeSession(sessionId); 435 if (mcRet != MC_DRV_OK) { 436 return mcRet; 437 } 438 439 // remove objects 440 removeTrustletSession(sessionId); 441 delete ts; 442 443 return MC_DRV_OK; 444} 445 446 447//------------------------------------------------------------------------------ 448mcResult_t MobiCoreDevice::mapBulk(uint32_t sessionId, uint32_t handle, uint32_t pAddrL2, 449 uint32_t offsetPayload, uint32_t lenBulkMem, uint32_t *secureVirtualAdr) 450{ 451 TrustletSession *ts = getTrustletSession(sessionId); 452 if (ts == NULL) { 453 LOG_E("no session found with id=%d", sessionId); 454 return MC_DRV_ERR_DAEMON_UNKNOWN_SESSION; 455 } 456 457 // TODO-2012-09-06-haenellu: Think about not ignoring the error case, ClientLib does not allow this. 458 ts->addBulkBuff(new CWsm((void *)offsetPayload, lenBulkMem, handle, (void *)pAddrL2)); 459 // Write MCP map message to buffer 460 mcpMessage->cmdMap.cmdHeader.cmdId = MC_MCP_CMD_MAP; 461 mcpMessage->cmdMap.sessionId = sessionId; 462 mcpMessage->cmdMap.wsmType = WSM_L2; 463 mcpMessage->cmdMap.adrBuffer = (uint32_t)(pAddrL2); 464 mcpMessage->cmdMap.ofsBuffer = offsetPayload; 465 mcpMessage->cmdMap.lenBuffer = lenBulkMem; 466 467 // Notify MC about the availability of a new command inside the MCP buffer 468 notify(SID_MCP); 469 470 // Wait till response from MC is available 471 if (!waitMcpNotification()) { 472 return MC_DRV_ERR_DAEMON_MCI_ERROR; 473 } 474 475 // Check if the command response ID is correct 476 if (mcpMessage->rspHeader.rspId != (MC_MCP_CMD_MAP | FLAG_RESPONSE)) { 477 LOG_E("CMD_MAP got invalid MCP response"); 478 return MC_DRV_ERR_DAEMON_MCI_ERROR; 479 } 480 481 uint32_t mcRet = mcpMessage->rspMap.rspHeader.result; 482 483 if (mcRet != MC_MCP_RET_OK) { 484 LOG_E("MCP MAP returned code %d.", mcRet); 485 return MAKE_MC_DRV_MCP_ERROR(mcRet); 486 } 487 488 *secureVirtualAdr = mcpMessage->rspMap.secureVirtualAdr; 489 return MC_DRV_OK; 490} 491 492 493//------------------------------------------------------------------------------ 494mcResult_t MobiCoreDevice::unmapBulk(uint32_t sessionId, uint32_t handle, 495 uint32_t secureVirtualAdr, uint32_t lenBulkMem) 496{ 497 TrustletSession *ts = getTrustletSession(sessionId); 498 if (ts == NULL) { 499 LOG_E("no session found with id=%d", sessionId); 500 return MC_DRV_ERR_DAEMON_UNKNOWN_SESSION; 501 } 502 503 // Write MCP unmap command to buffer 504 mcpMessage->cmdUnmap.cmdHeader.cmdId = MC_MCP_CMD_UNMAP; 505 mcpMessage->cmdUnmap.sessionId = sessionId; 506 mcpMessage->cmdUnmap.wsmType = WSM_L2; 507 mcpMessage->cmdUnmap.secureVirtualAdr = secureVirtualAdr; 508 mcpMessage->cmdUnmap.lenVirtualBuffer = lenBulkMem; 509 510 // Notify MC about the availability of a new command inside the MCP buffer 511 notify(SID_MCP); 512 513 // Wait till response from MC is available 514 if (!waitMcpNotification()) { 515 return MC_DRV_ERR_DAEMON_MCI_ERROR; 516 } 517 518 // Check if the command response ID is correct 519 if (mcpMessage->rspHeader.rspId != (MC_MCP_CMD_UNMAP | FLAG_RESPONSE)) { 520 LOG_E("CMD_OPEN_SESSION got invalid MCP response"); 521 return MC_DRV_ERR_DAEMON_MCI_ERROR; 522 } 523 524 uint32_t mcRet = mcpMessage->rspUnmap.rspHeader.result; 525 526 if (mcRet != MC_MCP_RET_OK) { 527 LOG_E("MCP UNMAP returned code %d.", mcRet); 528 return MAKE_MC_DRV_MCP_ERROR(mcRet); 529 } else { 530 // Just remove the buffer 531 // TODO-2012-09-06-haenellu: Haven't we removed it already? 532 if (!ts->removeBulkBuff(handle)) 533 LOG_I("unmapBulk(): no buffer found found with handle=%u", handle); 534 } 535 536 return MC_DRV_OK; 537} 538 539 540//------------------------------------------------------------------------------ 541void MobiCoreDevice::donateRam(const uint32_t donationSize) 542{ 543 // Donate additional RAM to the MobiCore 544 CWsm_ptr ram = allocateContiguousPersistentWsm(donationSize); 545 if (NULL == ram) { 546 LOG_E("Allocation of additional RAM failed"); 547 return; 548 } 549 ramType_t ramType = RAM_GENERIC; 550 addr_t adrBuffer = ram->physAddr; 551 const uint32_t numPages = donationSize / (4 * 1024); 552 553 554 LOG_I("donateRam(): adrBuffer=%p, numPages=%d, ramType=%d", 555 adrBuffer, 556 numPages, 557 ramType); 558 559 do { 560 // Write MCP open message to buffer 561 mcpMessage->cmdDonateRam.cmdHeader.cmdId = MC_MCP_CMD_DONATE_RAM; 562 mcpMessage->cmdDonateRam.adrBuffer = (uint32_t) adrBuffer; 563 mcpMessage->cmdDonateRam.numPages = numPages; 564 mcpMessage->cmdDonateRam.ramType = ramType; 565 566 // Notify MC about a new command inside the MCP buffer 567 notify(SID_MCP); 568 569 // Wait till response from MC is available 570 if (!waitMcpNotification()) { 571 break; 572 } 573 574 // Check if the command response ID is correct 575 if ((MC_MCP_CMD_DONATE_RAM | FLAG_RESPONSE) != mcpMessage->rspHeader.rspId) { 576 LOG_E("donateRam(): CMD_DONATE_RAM got invalid MCP response - rspId is: %d", 577 mcpMessage->rspHeader.rspId); 578 break; 579 } 580 581 uint32_t mcRet = mcpMessage->rspDonateRam.rspHeader.result; 582 if (MC_MCP_RET_OK != mcRet) { 583 LOG_E("donateRam(): CMD_DONATE_RAM error %d", mcRet); 584 break; 585 } 586 587 LOG_I("donateRam() succeeded."); 588 589 } while (0); 590} 591 592//------------------------------------------------------------------------------ 593mcResult_t MobiCoreDevice::getMobiCoreVersion( 594 mcDrvRspGetMobiCoreVersionPayload_ptr pRspGetMobiCoreVersionPayload 595) 596{ 597 // If MobiCore version info already fetched. 598 if (mcVersionInfo != NULL) { 599 pRspGetMobiCoreVersionPayload->versionInfo = *mcVersionInfo; 600 return MC_DRV_OK; 601 // Otherwise, fetch it via MCP. 602 } else { 603 // Write MCP unmap command to buffer 604 mcpMessage->cmdGetMobiCoreVersion.cmdHeader.cmdId = MC_MCP_CMD_GET_MOBICORE_VERSION; 605 606 // Notify MC about the availability of a new command inside the MCP buffer 607 notify(SID_MCP); 608 609 // Wait till response from MC is available 610 if (!waitMcpNotification()) { 611 return MC_DRV_ERR_DAEMON_MCI_ERROR; 612 } 613 614 // Check if the command response ID is correct 615 if ((MC_MCP_CMD_GET_MOBICORE_VERSION | FLAG_RESPONSE) != mcpMessage->rspHeader.rspId) { 616 LOG_E("MC_MCP_CMD_GET_MOBICORE_VERSION got invalid MCP response"); 617 return MC_DRV_ERR_DAEMON_MCI_ERROR; 618 } 619 620 uint32_t mcRet = mcpMessage->rspGetMobiCoreVersion.rspHeader.result; 621 622 if (mcRet != MC_MCP_RET_OK) { 623 LOG_E("MC_MCP_CMD_GET_MOBICORE_VERSION error %d", mcRet); 624 return MAKE_MC_DRV_MCP_ERROR(mcRet); 625 } 626 627 pRspGetMobiCoreVersionPayload->versionInfo = mcpMessage->rspGetMobiCoreVersion.versionInfo; 628 629 // Store MobiCore info for future reference. 630 mcVersionInfo = new mcVersionInfo_t(); 631 *mcVersionInfo = pRspGetMobiCoreVersionPayload->versionInfo; 632 return MC_DRV_OK; 633 } 634} 635 636//------------------------------------------------------------------------------ 637void MobiCoreDevice::queueUnknownNotification( 638 notification_t notification 639) 640{ 641 notifications.push(notification); 642} 643 644/** @} */ 645