| /*=========================================================================== |
| FILE: |
| GobiQMICore.cpp |
| |
| DESCRIPTION: |
| QUALCOMM Gobi QMI Based API Core |
| |
| PUBLIC CLASSES AND FUNCTIONS: |
| cGobiQMICore |
| |
| Copyright (c) 2011, Code Aurora Forum. All rights reserved. |
| |
| Redistribution and use in source and binary forms, with or without |
| modification, are permitted provided that the following conditions are met: |
| * Redistributions of source code must retain the above copyright |
| notice, this list of conditions and the following disclaimer. |
| * Redistributions in binary form must reproduce the above copyright |
| notice, this list of conditions and the following disclaimer in the |
| documentation and/or other materials provided with the distribution. |
| * Neither the name of Code Aurora Forum nor |
| the names of its contributors may be used to endorse or promote |
| products derived from this software without specific prior written |
| permission. |
| |
| |
| THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
| AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE |
| LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
| CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
| SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
| INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
| CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
| ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
| POSSIBILITY OF SUCH DAMAGE. |
| ==========================================================================*/ |
| |
| //--------------------------------------------------------------------------- |
| // Include Files |
| //--------------------------------------------------------------------------- |
| #if defined(linux) |
| #include <unistd.h> |
| #endif |
| |
| #include "StdAfx.h" |
| #include "GobiQMICore.h" |
| |
| #include "QMIBuffers.h" |
| #include "ProtocolNotification.h" |
| #include "CoreUtilities.h" |
| |
| //--------------------------------------------------------------------------- |
| // Definitions |
| //--------------------------------------------------------------------------- |
| |
| // Default timeout for Gobi QMI requests |
| const ULONG DEFAULT_GOBI_QMI_TIMEOUT = 2000; |
| |
| /*=========================================================================*/ |
| // Free Methods |
| /*=========================================================================*/ |
| |
| /*=========================================================================== |
| METHOD: |
| FindTLV (Free Method) |
| |
| DESCRIPTION: |
| Find the given TLV |
| |
| PARAMETERS: |
| tlvs [ I ] - TLV parsing input vector |
| tlvKey [ I ] - Key of the TLV that is to be found |
| |
| RETURN VALUE: |
| cDataParser::tParsedFields |
| ===========================================================================*/ |
| sDB2NavInput FindTLV( |
| const std::vector <sDB2NavInput> & tlvs, |
| const sProtocolEntityKey & tlvKey ) |
| { |
| sDB2NavInput retNI; |
| |
| // We need some TLVs to parse and a valid QMI DB key |
| ULONG tlvCount = (ULONG)tlvs.size(); |
| if (tlvCount == 0 || tlvKey.mKey.size() < 3) |
| { |
| return retNI; |
| } |
| |
| for (ULONG t = 0; t < tlvCount; t++) |
| { |
| const sDB2NavInput & ni = tlvs[t]; |
| if (tlvKey.mKey == ni.mKey) |
| { |
| retNI = ni; |
| break; |
| } |
| } |
| |
| return retNI; |
| } |
| |
| /*=========================================================================== |
| METHOD: |
| ParseTLV (Free Method) |
| |
| DESCRIPTION: |
| Parse the given TLV to fields |
| |
| PARAMETERS: |
| db [ I ] - Database to use |
| qmiBuf [ I ] - Original buffer containing TLV (locks data) |
| tlvs [ I ] - TLV parsing input vector |
| tlvKey [ I ] - Key of the TLV that is to be parsed |
| bFieldStrings [ I ] - Generate field value strings? |
| |
| RETURN VALUE: |
| cDataParser::tParsedFields |
| ===========================================================================*/ |
| cDataParser::tParsedFields ParseTLV( |
| const cCoreDatabase & db, |
| const sProtocolBuffer & qmiBuf, |
| const std::vector <sDB2NavInput> & tlvs, |
| const sProtocolEntityKey & tlvKey, |
| bool bFieldStrings ) |
| { |
| cDataParser::tParsedFields retFields; |
| |
| // We need some TLVs to parse and a valid QMI DB key |
| ULONG tlvCount = (ULONG)tlvs.size(); |
| if (tlvCount == 0 || tlvKey.mKey.size() < 3) |
| { |
| return retFields; |
| } |
| |
| for (ULONG t = 0; t < tlvCount; t++) |
| { |
| const sDB2NavInput & ni = tlvs[t]; |
| if (tlvKey.mKey == ni.mKey) |
| { |
| cDataParser dp( db, qmiBuf, tlvKey, ni.mpPayload, ni.mPayloadLen ); |
| dp.Parse( bFieldStrings, false ); |
| |
| retFields = dp.GetFields(); |
| break; |
| } |
| } |
| |
| return retFields; |
| } |
| |
| /*=========================================================================*/ |
| // cGobiQMICore Methods |
| /*=========================================================================*/ |
| |
| /*=========================================================================== |
| METHOD: |
| cGobiQMICore (Public Method) |
| |
| DESCRIPTION: |
| Constructor |
| |
| RETURN VALUE: |
| None |
| ===========================================================================*/ |
| cGobiQMICore::cGobiQMICore() |
| : mbFailOnMultipleDevices( false ), |
| mDeviceNode( "" ), |
| mDeviceKey( "" ), |
| mLastError( eGOBI_ERR_NONE ), |
| mRequests( 16 ), |
| mLastNetStartID( (WORD)INVALID_QMI_TRANSACTION_ID ), |
| mVid(0xBAADBEEF), mPid(0xCAFEBABE) |
| { |
| // Nothing to do |
| } |
| |
| /*=========================================================================== |
| METHOD: |
| ~cGobiQMICore (Public Method) |
| |
| DESCRIPTION: |
| Destructor |
| |
| RETURN VALUE: |
| BOOL |
| ===========================================================================*/ |
| cGobiQMICore::~cGobiQMICore() |
| { |
| Cleanup(); |
| } |
| |
| /*=========================================================================== |
| METHOD: |
| Initialize (Public Method) |
| |
| DESCRIPTION: |
| Initialize the object |
| |
| RETURN VALUE: |
| bool |
| ===========================================================================*/ |
| bool cGobiQMICore::Initialize() |
| { |
| // Initialize database |
| mDB.Initialize(); |
| |
| // Allocate configured QMI servers |
| bool bOK = true; |
| std::set <tServerConfig>::const_iterator pIter = mServerConfig.begin(); |
| while (pIter != mServerConfig.end()) |
| { |
| cQMIProtocolServer * pSvr = 0; |
| pSvr = new cQMIProtocolServer( pIter->first, 8192, 512 ); |
| if (pSvr == 0) |
| { |
| if (pIter->second == true) |
| { |
| bOK = false; |
| break; |
| } |
| } |
| else |
| { |
| mServers[pIter->first] = pSvr; |
| } |
| |
| pIter++; |
| } |
| |
| if (bOK == false) |
| { |
| Cleanup(); |
| } |
| |
| return bOK; |
| } |
| |
| /*=========================================================================== |
| METHOD: |
| Cleanup (Public Method) |
| |
| DESCRIPTION: |
| Cleanup the object |
| |
| RETURN VALUE: |
| bool |
| ===========================================================================*/ |
| bool cGobiQMICore::Cleanup() |
| { |
| Disconnect(); |
| |
| // Free allocated QMI servers |
| std::map <eQMIService, cQMIProtocolServer *>::const_iterator pIter; |
| pIter = mServers.begin(); |
| |
| while (pIter != mServers.end()) |
| { |
| cQMIProtocolServer * pSvr = pIter->second; |
| if (pSvr != 0) |
| { |
| delete pSvr; |
| } |
| |
| pIter++; |
| } |
| |
| mServers.clear(); |
| |
| return true; |
| } |
| |
| GobiType cGobiQMICore::GetDeviceType() |
| { |
| return ::GetDeviceType(mVid, mPid); |
| } |
| |
| /*=========================================================================== |
| METHOD: |
| GetAvailableDevices (Public Method) |
| |
| DESCRIPTION: |
| Return the set of available Gobi network devices |
| |
| RETURN VALUE: |
| std::vector <tDeviceID> - Vector of device ID and device key pairs |
| ===========================================================================*/ |
| std::vector <cGobiQMICore::tDeviceID> |
| cGobiQMICore::GetAvailableDevices() |
| { |
| std::vector <tDeviceID> devices; |
| |
| std::string path = "/sys/bus/usb/devices/"; |
| char buf[PATH_MAX]; |
| char *s; |
| |
| std::vector <std::string> files; |
| DepthSearch( path, |
| 3, |
| "qcqmi", |
| files ); |
| |
| int fileNum = files.size(); |
| for (int i = 0; i < fileNum; i++) |
| { |
| // Example "/sys/bus/usb/devices/8-1/8-1:1.0/GobiQMI/qcqmi0" |
| std::string nodePath = files[i]; |
| |
| int lastSlash = nodePath.find_last_of( "/" ); |
| |
| // This is what we want to return if everything else matches |
| std::string deviceNode = nodePath.substr( lastSlash + 1 ); |
| |
| // Move down two directories to the interface level |
| std::string curPath = nodePath.substr( 0, lastSlash ); |
| curPath = curPath.substr( 0, curPath.find_last_of( "/" ) ); |
| |
| // Read bInterfaceNumber |
| int handle = open( (curPath + "/bInterfaceNumber").c_str(), |
| O_RDONLY ); |
| if (handle == -1) |
| { |
| continue; |
| } |
| |
| char buff[4]; |
| memset( buff, 0, 4 ); |
| |
| bool bFound = false; |
| int ret = read( handle, buff, 2 ); |
| if (ret == 2) |
| { |
| ret = strncmp( buff, "00", 2 ); |
| if (ret == 0) |
| { |
| bFound = true; |
| } |
| |
| ret = strncmp( buff, "05", 2 ); |
| if (ret == 0) |
| { |
| bFound = true; |
| } |
| } |
| close( handle ); |
| |
| if (bFound == false) |
| { |
| continue; |
| } |
| |
| memset(buf, 0, sizeof(buf)); |
| if (readlink((curPath + "/driver").c_str(), buf, sizeof(buf)) < 0) |
| continue; |
| buf[sizeof(buf) - 1] = '\0'; |
| s = strrchr(buf, '/'); |
| s = s ? s + 1 : buf; |
| |
| if (strcmp(s, "gobi")) |
| continue; |
| |
| // Device node success! |
| |
| // Get MEID of device node (via ioctl) to use as key |
| std::string deviceStr = "/dev/" + deviceNode; |
| std::string key = cQMIProtocolServer::GetDeviceMEID( deviceStr ); |
| |
| tDeviceID device; |
| device.first = deviceNode; |
| device.second = key; |
| |
| devices.push_back( device ); |
| } |
| |
| return devices; |
| } |
| |
| static unsigned int getvidpid(const char *devname) |
| { |
| char buf[PATH_MAX + 1]; |
| char nbuf[PATH_MAX + 1]; |
| char idproduct[16]; |
| char idvendor[16]; |
| int fd; |
| |
| snprintf(buf, sizeof(buf), "/sys/class/QCQMI/%s/../../..", devname); |
| memset(nbuf, 0, sizeof(nbuf)); |
| if (!realpath(buf, nbuf)) { |
| return 0; |
| } |
| |
| snprintf(buf, sizeof(buf), "%s/idVendor", nbuf); |
| fd = open(buf, O_RDONLY); |
| if (fd < 0) { |
| return 0; |
| } |
| if (read(fd, idvendor, sizeof(idvendor)) <= 0) { |
| return 0; |
| } |
| close(fd); |
| |
| snprintf(buf, sizeof(buf), "%s/idProduct", nbuf); |
| fd = open(buf, O_RDONLY); |
| if (fd < 0) { |
| return 0; |
| } |
| if (read(fd, idproduct, sizeof(idproduct)) <= 0) { |
| return 0; |
| } |
| close(fd); |
| |
| return (strtoul(idvendor, NULL, 16) << 16) | strtoul(idproduct, NULL, 16); |
| } |
| |
| /*=========================================================================== |
| METHOD: |
| Connect (Public Method) |
| |
| DESCRIPTION: |
| Connect to the specified (or first detected) Gobi device |
| |
| Both device node and key are case sensitive |
| |
| PARAMETERS: |
| pDeviceNode [ I ] - The device node |
| pDeviceKey [ I ] - The device key (unique, stored on-device) |
| |
| RETURN VALUE: |
| bool |
| ===========================================================================*/ |
| bool cGobiQMICore::Connect( |
| LPCSTR pDeviceNode, |
| LPCSTR pDeviceKey ) |
| { |
| // Assume failure |
| bool bRC = false; |
| |
| // Clear last error recorded |
| ClearLastError(); |
| |
| // If you specify a device key then you have to specify a device ID |
| if (pDeviceNode == 0 && pDeviceKey != 0) |
| { |
| mLastError = eGOBI_ERR_INVALID_ARG; |
| return bRC; |
| } |
| |
| // First we terminate the current connection |
| Disconnect(); |
| |
| // Query system for list of active Gobi devices |
| std::vector <tDeviceID> devices = GetAvailableDevices(); |
| |
| // Did we find any devices? |
| ULONG deviceCount = (ULONG)devices.size(); |
| if (deviceCount == 0) |
| { |
| mLastError = eGOBI_ERR_NO_DEVICE; |
| return bRC; |
| } |
| |
| std::string deviceKey = ""; |
| if (pDeviceKey != 0) |
| { |
| deviceKey = pDeviceKey; |
| } |
| |
| // Filter that list to include only the specified device? |
| if (pDeviceNode != 0) |
| { |
| std::vector <tDeviceID>::iterator current = devices.begin(); |
| while (current != devices.end()) |
| { |
| // Remove if device node doesn't match |
| if (current->first.compare( pDeviceNode ) != 0) |
| { |
| // Erase current element and update ourself to point to next |
| current = devices.erase( current ); |
| } |
| // Remove if invalid key is specified |
| else if (deviceKey.size() != 0 |
| && current->second.compare( deviceKey ) != 0) |
| { |
| current = devices.erase( current ); |
| } |
| // All necessary parameters match |
| else |
| { |
| current++; |
| } |
| } |
| } |
| |
| // Anything left after filtering? |
| deviceCount = (ULONG)devices.size(); |
| if (deviceCount == 0) |
| { |
| mLastError = eGOBI_ERR_NO_DEVICE; |
| return bRC; |
| } |
| |
| // Too many to choose from? |
| if (deviceCount > 1 && mbFailOnMultipleDevices == true) |
| { |
| mLastError = eGOBI_ERR_MULTIPLE_DEVICES; |
| return bRC; |
| } |
| |
| // Store device ID/key strings |
| mDeviceNode = devices[0].first; |
| mDeviceKey = devices[0].second; |
| |
| unsigned int vidpid = getvidpid(mDeviceNode.c_str()); |
| mVid = (vidpid >> 16) & 0xFFFF; |
| mPid = vidpid & 0xFFFF; |
| |
| // Initalize/connect all configured QMI servers |
| std::map <eQMIService, cQMIProtocolServer *>::const_iterator pIter; |
| pIter = mServers.begin(); |
| |
| while (pIter != mServers.end()) |
| { |
| cQMIProtocolServer * pSvr = pIter->second; |
| if (pSvr != 0) |
| { |
| // Initialize server (we don't care about the return code |
| // since the following Connect() call will fail if we are |
| // unable to initialize the server) |
| pSvr->Initialize(); |
| |
| std::string deviceStr = "/dev/" + mDeviceNode; |
| bRC = pSvr->Connect( deviceStr.c_str() ); |
| if (bRC == false) |
| { |
| tServerConfig tsc( pIter->first, true ); |
| if (mServerConfig.find( tsc ) != mServerConfig.end()) |
| { |
| // Failure on essential server |
| break; |
| } |
| else |
| { |
| // QMI server non-essential (ignore failure) |
| bRC = true; |
| } |
| } |
| } |
| |
| pIter++; |
| } |
| |
| // Any server fail? |
| if (bRC == false) |
| { |
| // Yes, disconnect them all |
| Disconnect(); |
| |
| // ... and set the error code |
| mLastError = eGOBI_ERR_CONNECT; |
| } |
| |
| return bRC; |
| } |
| |
| /*=========================================================================== |
| METHOD: |
| Disconnect (Public Method) |
| |
| DESCRIPTION: |
| Disconnect from the currently connected Gobi device |
| |
| RETURN VALUE: |
| bool |
| ===========================================================================*/ |
| bool cGobiQMICore::Disconnect() |
| { |
| // Clear last error recorded |
| ClearLastError(); |
| |
| // Assume failure |
| bool bRC = false; |
| if (mDeviceNode.size() > 0) |
| { |
| mDeviceNode.clear(); |
| mDeviceKey.clear(); |
| bRC = true; |
| } |
| else |
| { |
| mLastError = eGOBI_ERR_NO_CONNECTION; |
| } |
| |
| // Disconnect/clean-up all configured QMI servers |
| std::map <eQMIService, cQMIProtocolServer *>::const_iterator pIter; |
| pIter = mServers.begin(); |
| |
| while (pIter != mServers.end()) |
| { |
| cQMIProtocolServer * pSvr = pIter->second; |
| if (pSvr != 0) |
| { |
| pSvr->Disconnect(); |
| pSvr->Exit(); |
| } |
| |
| pIter++; |
| } |
| |
| mVid = 0xDEADD00D; |
| mPid = 0xDEADD00D; |
| |
| return bRC; |
| } |
| |
| /*=========================================================================== |
| METHOD: |
| GetConnectedDeviceID (Public Method) |
| |
| DESCRIPTION: |
| Get the device node/key of the currently connected Gobi device |
| |
| PARAMETERS: |
| devNode [ O ] - Device node (IE: qcqmi0) |
| devKey [ O ] - Device key (may be empty) |
| |
| RETURN VALUE: |
| bool |
| ===========================================================================*/ |
| bool cGobiQMICore::GetConnectedDeviceID( |
| std::string & devNode, |
| std::string & devKey ) |
| { |
| // Clear last error recorded |
| ClearLastError(); |
| |
| // Assume failure |
| bool bFound = false; |
| devNode.clear(); |
| devKey.clear(); |
| |
| // Are all required servers connected? |
| bool bAllConnected = true; |
| |
| std::map <eQMIService, cQMIProtocolServer *>::const_iterator pIter; |
| pIter = mServers.begin(); |
| |
| while (pIter != mServers.end()) |
| { |
| tServerConfig tsc( pIter->first, true ); |
| cQMIProtocolServer * pSvr = pIter->second; |
| |
| if (mServerConfig.find( tsc ) != mServerConfig.end() && pSvr != 0) |
| { |
| if (pSvr->IsConnected() == false) |
| { |
| // Failure on essential server |
| bAllConnected = false; |
| break; |
| } |
| } |
| |
| pIter++; |
| } |
| |
| // Were we once connected? |
| if (mDeviceNode.size() > 0 && bAllConnected == true) |
| { |
| // Yes, but is our device still present? |
| // NOTE: This does not guarantee the device did not leave and come back |
| std::vector <tDeviceID> devices = GetAvailableDevices(); |
| ULONG deviceCount = (ULONG)devices.size(); |
| |
| for (ULONG a = 0; a < deviceCount; a++) |
| { |
| if (devices[a].first == mDeviceNode) |
| { |
| // If there is a device key specified, it must match. |
| if (mDeviceKey.size() > 0) |
| { |
| if (devices[a].second == mDeviceKey) |
| { |
| devNode = devices[a].first; |
| devKey = devices[a].second; |
| |
| bFound = true; |
| break; |
| } |
| } |
| else |
| { |
| devNode = devices[a].first; |
| |
| bFound = true; |
| break; |
| } |
| } |
| } |
| |
| if (bFound == false) |
| { |
| mLastError = eGOBI_ERR_NO_DEVICE; |
| } |
| } |
| else |
| { |
| // We are not connected |
| mLastError = eGOBI_ERR_NO_CONNECTION; |
| } |
| |
| return bFound; |
| } |
| |
| /*=========================================================================== |
| METHOD: |
| Send (Public Method) |
| |
| DESCRIPTION: |
| Send a request using the specified QMI protocol server and wait for (and |
| then return) the response |
| |
| PARAMETERS: |
| svc [ I ] - QMI service type |
| pRequest [ I ] - Request to schedule |
| to [ I ] - Timeout value (in milliseconds) |
| |
| RETURN VALUE: |
| sProtocolBuffer - The response (invalid when no response was received) |
| ===========================================================================*/ |
| sProtocolBuffer cGobiQMICore::Send( |
| eQMIService svc, |
| sSharedBuffer * pRequest, |
| ULONG to ) |
| { |
| // Clear last error recorded |
| ClearLastError(); |
| |
| // Returned response |
| sProtocolBuffer rsp; |
| |
| // Validate the arguments |
| if (pRequest == 0) |
| { |
| mLastError = eGOBI_ERR_MEMORY; |
| return rsp; |
| } |
| |
| // We use the event based notification approach |
| cSyncQueue <sProtocolNotificationEvent> evts( 12, true ); |
| cProtocolQueueNotification pn( &evts ); |
| |
| // Process up to the indicated timeout |
| cEvent & sigEvt = evts.GetSignalEvent(); |
| |
| // Build the request object |
| sProtocolRequest req( pRequest, 0, to, 1, 1, &pn ); |
| if (to == 0) |
| { |
| mLastError = eGOBI_ERR_INTERNAL; |
| return rsp; |
| } |
| |
| // Grab the server |
| cQMIProtocolServer * pSvr = GetServer( svc ); |
| if (pSvr == 0) |
| { |
| mLastError = eGOBI_ERR_INTERNAL; |
| return rsp; |
| } |
| |
| // Are we connected? |
| if (mDeviceNode.size() <= 0 || pSvr->IsConnected() == false) |
| { |
| mLastError = eGOBI_ERR_NO_CONNECTION; |
| return rsp; |
| } |
| |
| // Grab the log from the server |
| const cProtocolLog & protocolLog = pSvr->GetLog(); |
| |
| // Schedule the request |
| ULONG reqID = pSvr->AddRequest( req ); |
| if (reqID == INVALID_REQUEST_ID) |
| { |
| mLastError = eGOBI_ERR_REQ_SCHEDULE; |
| return rsp; |
| } |
| |
| // Store for external cancel |
| tServiceRequest sr( svc, reqID ); |
| mRequests.AddElement( sr ); |
| |
| bool bReq = false; |
| bool bExit = false; |
| DWORD idx; |
| |
| // Process up to the indicated timeout |
| while (bExit == false) |
| { |
| int wc = sigEvt.Wait( to, idx ); |
| if (wc == ETIME) |
| { |
| if (bReq == true) |
| { |
| mLastError = eGOBI_ERR_RESPONSE_TO; |
| } |
| else |
| { |
| mLastError = eGOBI_ERR_REQUEST_TO; |
| } |
| break; |
| } |
| else if (wc != 0) |
| { |
| mLastError = eGOBI_ERR_INTERNAL; |
| break; |
| } |
| |
| sProtocolNotificationEvent evt; |
| bool bEvt = evts.GetElement( idx, evt ); |
| if (bEvt == false) |
| { |
| mLastError = eGOBI_ERR_INTERNAL; |
| bExit = true; |
| break; |
| } |
| |
| switch (evt.mEventType) |
| { |
| case ePROTOCOL_EVT_REQ_ERR: |
| mLastError = eGOBI_ERR_REQUEST; |
| bExit = true; |
| break; |
| |
| case ePROTOCOL_EVT_RSP_ERR: |
| mLastError = eGOBI_ERR_RESPONSE; |
| bExit = true; |
| break; |
| |
| case ePROTOCOL_EVT_REQ_SENT: |
| { |
| // Are we doing WDS business? |
| if (svc == eQMI_SVC_WDS) |
| { |
| // Grab the as-sent request |
| DWORD id = evt.mParam2; |
| sProtocolBuffer tmpReq = protocolLog.GetBuffer( id ); |
| sSharedBuffer * pTmpRequest = tmpReq.GetSharedBuffer(); |
| if (pTmpRequest != 0) |
| { |
| // Check the message ID |
| sQMIServiceBuffer actualReq( pTmpRequest ); |
| ULONG msgID = actualReq.GetMessageID(); |
| if (msgID == (ULONG)eQMI_WDS_START_NET) |
| { |
| // Grab the transaction ID |
| mLastNetStartID = actualReq.GetTransactionID(); |
| } |
| } |
| } |
| |
| bReq = true; |
| } |
| break; |
| |
| case ePROTOCOL_EVT_RSP_RECV: |
| // Success! |
| rsp = protocolLog.GetBuffer( evt.mParam2 ); |
| bExit = true; |
| break; |
| } |
| } |
| |
| if ( (mLastError == eGOBI_ERR_INTERNAL) |
| || (mLastError == eGOBI_ERR_REQUEST_TO) |
| || (mLastError == eGOBI_ERR_RESPONSE_TO) ) |
| { |
| // Remove the request as our protocol notification object is |
| // about to go out of scope and hence be destroyed |
| pSvr->RemoveRequest( reqID ); |
| } |
| |
| // Check that the device is still there? |
| if ( (mLastError == eGOBI_ERR_REQUEST) |
| || (mLastError == eGOBI_ERR_RESPONSE) |
| || (mLastError == eGOBI_ERR_REQUEST_TO) |
| || (mLastError == eGOBI_ERR_RESPONSE_TO) ) |
| { |
| eGobiError tmp = mLastError; |
| |
| std::string dummy; |
| GetConnectedDeviceID( dummy, dummy ); |
| if (mLastError == eGOBI_ERR_NONE) |
| { |
| mLastError = tmp; |
| } |
| } |
| |
| return rsp; |
| } |
| |
| /*=========================================================================== |
| METHOD: |
| SendAndCheckReturn (Public Method) |
| |
| DESCRIPTION: |
| Send a request using the specified QMI protocol server and wait for (and |
| then validate) the response |
| |
| PARAMETERS: |
| svc [ I ] - QMI service type |
| pRequest [ I ] - Request to schedule |
| to [ I ] - Timeout value (in milliseconds) |
| |
| RETURN VALUE: |
| eGobiError - Corrected error code |
| ===========================================================================*/ |
| eGobiError cGobiQMICore::SendAndCheckReturn( |
| eQMIService svc, |
| sSharedBuffer * pRequest, |
| ULONG to ) |
| { |
| sProtocolBuffer rsp = Send( svc, pRequest, to ); |
| if (rsp.IsValid() == false) |
| { |
| return GetCorrectedLastError(); |
| } |
| |
| // Did we receive a valid QMI response? |
| sQMIServiceBuffer qmiRsp( rsp.GetSharedBuffer() ); |
| if (qmiRsp.IsValid() == false) |
| { |
| mLastError = eGOBI_ERR_MALFORMED_RSP; |
| return mLastError; |
| } |
| |
| // Check the mandatory QMI result TLV for success |
| ULONG rc = 0; |
| ULONG ec = 0; |
| bool bResult = qmiRsp.GetResult( rc, ec ); |
| if (bResult == false) |
| { |
| mLastError = eGOBI_ERR_MALFORMED_RSP; |
| return mLastError; |
| } |
| else if (rc != 0) |
| { |
| return GetCorrectedQMIError( ec ); |
| } |
| |
| // Success! |
| return eGOBI_ERR_NONE; |
| } |
| |
| /*=========================================================================== |
| METHOD: |
| SendSimple (Public Method) |
| |
| DESCRIPTION: |
| Generate/send a request using the specified QMI protocol server and |
| wait for (and then return) the response |
| |
| PARAMETERS: |
| svc [ I ] - QMI service type |
| msgID [ I ] - QMI message ID of the request to generate |
| to [ I ] - Timeout value (in milliseconds) |
| |
| NOTE: The request has to be a single byte in length, i.e. just a |
| command code, in order for success |
| |
| RETURN VALUE: |
| sProtocolBuffer - The response (invalid when no response was received) |
| ===========================================================================*/ |
| sProtocolBuffer cGobiQMICore::SendSimple( |
| eQMIService svc, |
| WORD msgID, |
| ULONG to ) |
| { |
| // Clear last error recorded |
| ClearLastError(); |
| |
| sProtocolBuffer rsp; |
| |
| sSharedBuffer * pReq = 0; |
| pReq = sQMIServiceBuffer::BuildBuffer( svc, msgID ); |
| if (pReq == 0) |
| { |
| mLastError = eGOBI_ERR_MEMORY; |
| return rsp; |
| } |
| |
| rsp = Send( svc, pReq, to ); |
| return rsp; |
| } |
| |
| /*=========================================================================== |
| METHOD: |
| CancelSend (Public Method) |
| |
| DESCRIPTION: |
| Cancel the most recent in-progress Send() based operation |
| |
| RETURN VALUE: |
| eGobiError |
| ===========================================================================*/ |
| eGobiError cGobiQMICore::CancelSend() |
| { |
| ULONG reqs = mRequests.GetTotalCount(); |
| if (reqs == 0) |
| { |
| return eGOBI_ERR_NO_CANCELABLE_OP; |
| } |
| |
| tServiceRequest elem( eQMI_SVC_ENUM_BEGIN, INVALID_REQUEST_ID ); |
| bool bElem = mRequests.GetElement( --reqs, elem ); |
| if (bElem == false) |
| { |
| return eGOBI_ERR_INTERNAL; |
| } |
| |
| cQMIProtocolServer * pSvr = GetServer( elem.first ); |
| if (pSvr == 0) |
| { |
| return eGOBI_ERR_INTERNAL; |
| } |
| |
| |
| bool bRemove = pSvr->RemoveRequest( elem.second ); |
| if (bRemove == false) |
| { |
| return eGOBI_ERR_CANCEL_OP; |
| } |
| |
| return eGOBI_ERR_NONE; |
| } |