#ifdef __QUEC_INCLUDE_QMI_C__ /*=========================================================================== FILE: QMI.c DESCRIPTION: Qualcomm QMI driver code FUNCTIONS: Generic QMUX functions ParseQMUX FillQMUX Generic QMI functions GetTLV ValidQMIMessage GetQMIMessageID Fill Buffers with QMI requests QMICTLGetClientIDReq QMICTLReleaseClientIDReq QMICTLReadyReq QMIWDSSetEventReportReq QMIWDSGetPKGSRVCStatusReq QMIDMSGetMEIDReq QMIWDASetDataFormatReq QMICTLSetDataFormatReq QMICTLSyncReq Parse data from QMI responses QMICTLGetClientIDResp QMICTLReleaseClientIDResp QMIWDSEventResp QMIDMSGetMEIDResp QMIWDASetDataFormatResp QMICTLSyncResp 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 //--------------------------------------------------------------------------- #include #include #include "Structs.h" #include "QMI.h" /*=========================================================================*/ // Get sizes of buffers needed by QMI requests /*=========================================================================*/ /*=========================================================================== METHOD: QMUXHeaderSize (Public Method) DESCRIPTION: Get size of buffer needed for QMUX RETURN VALUE: u16 - size of buffer ===========================================================================*/ static u16 QMUXHeaderSize( void ) { return sizeof( sQMUX ); } /*=========================================================================== METHOD: QMICTLGetClientIDReqSize (Public Method) DESCRIPTION: Get size of buffer needed for QMUX + QMICTLGetClientIDReq RETURN VALUE: u16 - size of buffer ===========================================================================*/ static u16 QMICTLGetClientIDReqSize( void ) { return sizeof( sQMUX ) + 10; } /*=========================================================================== METHOD: QMICTLReleaseClientIDReqSize (Public Method) DESCRIPTION: Get size of buffer needed for QMUX + QMICTLReleaseClientIDReq RETURN VALUE: u16 - size of header ===========================================================================*/ static u16 QMICTLReleaseClientIDReqSize( void ) { return sizeof( sQMUX ) + 11; } /*=========================================================================== METHOD: QMICTLReadyReqSize (Public Method) DESCRIPTION: Get size of buffer needed for QMUX + QMICTLReadyReq RETURN VALUE: u16 - size of buffer ===========================================================================*/ static u16 QMICTLReadyReqSize( void ) { return sizeof( sQMUX ) + 6; } /*=========================================================================== METHOD: QMIWDSSetEventReportReqSize (Public Method) DESCRIPTION: Get size of buffer needed for QMUX + QMIWDSSetEventReportReq RETURN VALUE: u16 - size of buffer ===========================================================================*/ static u16 QMIWDSSetEventReportReqSize( void ) { return sizeof( sQMUX ) + 15; } /*=========================================================================== METHOD: QMIWDSGetPKGSRVCStatusReqSize (Public Method) DESCRIPTION: Get size of buffer needed for QMUX + QMIWDSGetPKGSRVCStatusReq RETURN VALUE: u16 - size of buffer ===========================================================================*/ static u16 QMIWDSGetPKGSRVCStatusReqSize( void ) { return sizeof( sQMUX ) + 7; } /*=========================================================================== METHOD: QMIDMSGetMEIDReqSize (Public Method) DESCRIPTION: Get size of buffer needed for QMUX + QMIDMSGetMEIDReq RETURN VALUE: u16 - size of buffer ===========================================================================*/ static u16 QMIDMSGetMEIDReqSize( void ) { return sizeof( sQMUX ) + 7; } #ifdef QUECTEL_WWAN_QMAP struct QMIWDS_ADMIN_SET_DATA_FORMAT_TLV_QOS { u8 TLVType; u16 TLVLength; u8 QOSSetting; } __packed; struct QMIWDS_ADMIN_SET_DATA_FORMAT_TLV { u8 TLVType; u16 TLVLength; u32 Value; } __packed; struct QMIWDS_ENDPOINT_TLV { u8 TLVType; u16 TLVLength; u32 ep_type; u32 iface_id; } __packed; struct QMIWDS_ADMIN_SET_DATA_FORMAT_REQ_MSG { u8 CtlFlags; // 0: single QMUX Msg; 1: u16 TransactionId; u16 Type; u16 Length; struct QMIWDS_ADMIN_SET_DATA_FORMAT_TLV_QOS QosDataFormatTlv; struct QMIWDS_ADMIN_SET_DATA_FORMAT_TLV UnderlyingLinkLayerProtocolTlv; struct QMIWDS_ADMIN_SET_DATA_FORMAT_TLV UplinkDataAggregationProtocolTlv; struct QMIWDS_ADMIN_SET_DATA_FORMAT_TLV DownlinkDataAggregationProtocolTlv; struct QMIWDS_ADMIN_SET_DATA_FORMAT_TLV DownlinkDataAggregationMaxDatagramsTlv; struct QMIWDS_ADMIN_SET_DATA_FORMAT_TLV DownlinkDataAggregationMaxSizeTlv; struct QMIWDS_ENDPOINT_TLV epTlv; //struct QMIWDS_ADMIN_SET_DATA_FORMAT_TLV dl_minimum_padding; } __packed; #endif /*=========================================================================== METHOD: QMIWDASetDataFormatReqSize (Public Method) DESCRIPTION: Get size of buffer needed for QMUX + QMIWDASetDataFormatReq RETURN VALUE: u16 - size of buffer ===========================================================================*/ static u16 QMIWDASetDataFormatReqSize( int qmap_mode ) { if (qmap_mode) return sizeof( sQMUX ) + sizeof(struct QMIWDS_ADMIN_SET_DATA_FORMAT_REQ_MSG); else return sizeof( sQMUX ) + 18; } /*=========================================================================== METHOD: QMICTLSyncReqSize (Public Method) DESCRIPTION: Get size of buffer needed for QMUX + QMICTLSyncReq RETURN VALUE: u16 - size of buffer ===========================================================================*/ static u16 QMICTLSyncReqSize( void ) { return sizeof( sQMUX ) + 6; } /*=========================================================================*/ // Generic QMUX functions /*=========================================================================*/ /*=========================================================================== METHOD: ParseQMUX (Public Method) DESCRIPTION: Remove QMUX headers from a buffer PARAMETERS pClientID [ O ] - On success, will point to Client ID pBuffer [ I ] - Full Message passed in buffSize [ I ] - Size of pBuffer RETURN VALUE: int - Positive for size of QMUX header Negative errno for error ===========================================================================*/ static int ParseQMUX( u16 * pClientID, void * pBuffer, u16 buffSize ) { sQMUX * pQMUXHeader; if (pBuffer == 0 || buffSize < 12) { return -ENOMEM; } // QMUX Header pQMUXHeader = (sQMUX *)pBuffer; if (pQMUXHeader->mTF != 1 || le16_to_cpu(get_unaligned(&pQMUXHeader->mLength)) != buffSize - 1 || pQMUXHeader->mCtrlFlag != 0x80 ) { return -EINVAL; } // Client ID *pClientID = (pQMUXHeader->mQMIClientID << 8) + pQMUXHeader->mQMIService; return sizeof( sQMUX ); } /*=========================================================================== METHOD: FillQMUX (Public Method) DESCRIPTION: Fill buffer with QMUX headers PARAMETERS clientID [ I ] - Client ID pBuffer [ O ] - Buffer to be filled buffSize [ I ] - Size of pBuffer (must be at least 6) RETURN VALUE: int - 0 for success Negative errno for error ===========================================================================*/ static int FillQMUX( u16 clientID, void * pBuffer, u16 buffSize ) { sQMUX * pQMUXHeader; if (pBuffer == 0 || buffSize < sizeof( sQMUX )) { return -ENOMEM; } // QMUX Header pQMUXHeader = (sQMUX *)pBuffer; pQMUXHeader->mTF = 1; put_unaligned(cpu_to_le16(buffSize - 1), &pQMUXHeader->mLength); //DBG("pQMUXHeader->mLength = 0x%x, buffSize - 1 = 0x%x\n",pQMUXHeader->mLength, buffSize - 1); pQMUXHeader->mCtrlFlag = 0; // Service and Client ID pQMUXHeader->mQMIService = clientID & 0xff; pQMUXHeader->mQMIClientID = clientID >> 8; return 0; } /*=========================================================================*/ // Generic QMI functions /*=========================================================================*/ /*=========================================================================== METHOD: GetTLV (Public Method) DESCRIPTION: Get data buffer of a specified TLV from a QMI message QMI Message shall NOT include SDU PARAMETERS pQMIMessage [ I ] - QMI Message buffer messageLen [ I ] - Size of QMI Message buffer type [ I ] - Desired Type pOutDataBuf [ O ] - Buffer to be filled with TLV messageLen [ I ] - Size of QMI Message buffer RETURN VALUE: u16 - Size of TLV for success Negative errno for error ===========================================================================*/ static int GetTLV( void * pQMIMessage, u16 messageLen, u8 type, void * pOutDataBuf, u16 bufferLen ) { u16 pos; u16 tlvSize = 0; u16 cpyCount; if (pQMIMessage == 0 || pOutDataBuf == 0) { return -ENOMEM; } for (pos = 4; pos + 3 < messageLen; pos += tlvSize + 3) { tlvSize = le16_to_cpu( get_unaligned(((u16 *)(pQMIMessage + pos + 1) )) ); if (*(u8 *)(pQMIMessage + pos) == type) { if (bufferLen < tlvSize) { return -ENOMEM; } for (cpyCount = 0; cpyCount < tlvSize; cpyCount++) { *((char*)(pOutDataBuf + cpyCount)) = *((char*)(pQMIMessage + pos + 3 + cpyCount)); } return tlvSize; } } return -ENOMSG; } /*=========================================================================== METHOD: ValidQMIMessage (Public Method) DESCRIPTION: Check mandatory TLV in a QMI message QMI Message shall NOT include SDU PARAMETERS pQMIMessage [ I ] - QMI Message buffer messageLen [ I ] - Size of QMI Message buffer RETURN VALUE: int - 0 for success (no error) Negative errno for error Positive for QMI error code ===========================================================================*/ static int ValidQMIMessage( void * pQMIMessage, u16 messageLen ) { char mandTLV[4]; if (GetTLV( pQMIMessage, messageLen, 2, &mandTLV[0], 4 ) == 4) { // Found TLV if (*(u16 *)&mandTLV[0] != 0) { return le16_to_cpu( get_unaligned(&mandTLV[2]) ); } else { return 0; } } else { return -ENOMSG; } } /*=========================================================================== METHOD: GetQMIMessageID (Public Method) DESCRIPTION: Get the message ID of a QMI message QMI Message shall NOT include SDU PARAMETERS pQMIMessage [ I ] - QMI Message buffer messageLen [ I ] - Size of QMI Message buffer RETURN VALUE: int - Positive for message ID Negative errno for error ===========================================================================*/ static int GetQMIMessageID( void * pQMIMessage, u16 messageLen ) { if (messageLen < 2) { return -ENODATA; } else { return le16_to_cpu( get_unaligned((u16 *)pQMIMessage) ); } } /*=========================================================================*/ // Fill Buffers with QMI requests /*=========================================================================*/ /*=========================================================================== METHOD: QMICTLGetClientIDReq (Public Method) DESCRIPTION: Fill buffer with QMI CTL Get Client ID Request PARAMETERS pBuffer [ 0 ] - Buffer to be filled buffSize [ I ] - Size of pBuffer transactionID [ I ] - Transaction ID serviceType [ I ] - Service type requested RETURN VALUE: int - Positive for resulting size of pBuffer Negative errno for error ===========================================================================*/ static int QMICTLGetClientIDReq( void * pBuffer, u16 buffSize, u8 transactionID, u8 serviceType ) { if (pBuffer == 0 || buffSize < QMICTLGetClientIDReqSize() ) { return -ENOMEM; } // QMI CTL GET CLIENT ID // Request *(u8 *)(pBuffer + sizeof( sQMUX ))= 0x00; // Transaction ID *(u8 *)(pBuffer + sizeof( sQMUX ) + 1) = transactionID; // Message ID put_unaligned(cpu_to_le16(0x0022), (u16 *)(pBuffer + sizeof( sQMUX ) + 2)); // Size of TLV's put_unaligned(cpu_to_le16(0x0004), (u16 *)(pBuffer + sizeof( sQMUX ) + 4)); // QMI Service Type *(u8 *)(pBuffer + sizeof( sQMUX ) + 6) = 0x01; // Size put_unaligned(cpu_to_le16(0x0001), (u16 *)(pBuffer + sizeof( sQMUX ) + 7)); // QMI svc type *(u8 *)(pBuffer + sizeof( sQMUX ) + 9) = serviceType; // success return sizeof( sQMUX ) + 10; } /*=========================================================================== METHOD: QMICTLReleaseClientIDReq (Public Method) DESCRIPTION: Fill buffer with QMI CTL Release Client ID Request PARAMETERS pBuffer [ 0 ] - Buffer to be filled buffSize [ I ] - Size of pBuffer transactionID [ I ] - Transaction ID clientID [ I ] - Service type requested RETURN VALUE: int - Positive for resulting size of pBuffer Negative errno for error ===========================================================================*/ static int QMICTLReleaseClientIDReq( void * pBuffer, u16 buffSize, u8 transactionID, u16 clientID ) { if (pBuffer == 0 || buffSize < QMICTLReleaseClientIDReqSize() ) { return -ENOMEM; } DBG( "buffSize: 0x%x, transactionID: 0x%x, clientID: 0x%x,\n", buffSize, transactionID, clientID ); // QMI CTL RELEASE CLIENT ID REQ // Request *(u8 *)(pBuffer + sizeof( sQMUX )) = 0x00; // Transaction ID *(u8 *)(pBuffer + sizeof( sQMUX ) + 1 ) = transactionID; // Message ID put_unaligned( cpu_to_le16(0x0023), (u16 *)(pBuffer + sizeof( sQMUX ) + 2) ); // Size of TLV's put_unaligned( cpu_to_le16(0x0005), (u16 *)(pBuffer + sizeof( sQMUX ) + 4) ); // Release client ID *(u8 *)(pBuffer + sizeof( sQMUX ) + 6) = 0x01; // Size put_unaligned( cpu_to_le16(0x0002), (u16 *)(pBuffer + sizeof( sQMUX ) + 7)); // QMI svs type / Client ID put_unaligned(cpu_to_le16(clientID), (u16 *)(pBuffer + sizeof( sQMUX ) + 9)); // success return sizeof( sQMUX ) + 11; } /*=========================================================================== METHOD: QMICTLReadyReq (Public Method) DESCRIPTION: Fill buffer with QMI CTL Get Version Info Request PARAMETERS pBuffer [ 0 ] - Buffer to be filled buffSize [ I ] - Size of pBuffer transactionID [ I ] - Transaction ID RETURN VALUE: int - Positive for resulting size of pBuffer Negative errno for error ===========================================================================*/ static int QMICTLReadyReq( void * pBuffer, u16 buffSize, u8 transactionID ) { if (pBuffer == 0 || buffSize < QMICTLReadyReqSize() ) { return -ENOMEM; } DBG("buffSize: 0x%x, transactionID: 0x%x\n", buffSize, transactionID); // QMI CTL GET VERSION INFO REQ // Request *(u8 *)(pBuffer + sizeof( sQMUX )) = 0x00; // Transaction ID *(u8 *)(pBuffer + sizeof( sQMUX ) + 1) = transactionID; // Message ID put_unaligned( cpu_to_le16(0x0021), (u16 *)(pBuffer + sizeof( sQMUX ) + 2) ); // Size of TLV's put_unaligned( cpu_to_le16(0x0000), (u16 *)(pBuffer + sizeof( sQMUX ) + 4) ); // success return sizeof( sQMUX ) + 6; } /*=========================================================================== METHOD: QMIWDSSetEventReportReq (Public Method) DESCRIPTION: Fill buffer with QMI WDS Set Event Report Request PARAMETERS pBuffer [ 0 ] - Buffer to be filled buffSize [ I ] - Size of pBuffer transactionID [ I ] - Transaction ID RETURN VALUE: int - Positive for resulting size of pBuffer Negative errno for error ===========================================================================*/ static int QMIWDSSetEventReportReq( void * pBuffer, u16 buffSize, u16 transactionID ) { if (pBuffer == 0 || buffSize < QMIWDSSetEventReportReqSize() ) { return -ENOMEM; } // QMI WDS SET EVENT REPORT REQ // Request *(u8 *)(pBuffer + sizeof( sQMUX )) = 0x00; // Transaction ID put_unaligned( cpu_to_le16(transactionID), (u16 *)(pBuffer + sizeof( sQMUX ) + 1)); // Message ID put_unaligned( cpu_to_le16(0x0001), (u16 *)(pBuffer + sizeof( sQMUX ) + 3)); // Size of TLV's put_unaligned(cpu_to_le16(0x0008), (u16 *)(pBuffer + sizeof( sQMUX ) + 5)); // Report channel rate TLV *(u8 *)(pBuffer + sizeof( sQMUX ) + 7) = 0x11; // Size put_unaligned( cpu_to_le16(0x0005), (u16 *)(pBuffer + sizeof( sQMUX ) + 8)); // Stats period *(u8 *)(pBuffer + sizeof( sQMUX ) + 10) = 0x01; // Stats mask put_unaligned( cpu_to_le32(0x000000ff), (u32 *)(pBuffer + sizeof( sQMUX ) + 11) ); // success return sizeof( sQMUX ) + 15; } /*=========================================================================== METHOD: QMIWDSGetPKGSRVCStatusReq (Public Method) DESCRIPTION: Fill buffer with QMI WDS Get PKG SRVC Status Request PARAMETERS pBuffer [ 0 ] - Buffer to be filled buffSize [ I ] - Size of pBuffer transactionID [ I ] - Transaction ID RETURN VALUE: int - Positive for resulting size of pBuffer Negative errno for error ===========================================================================*/ static int QMIWDSGetPKGSRVCStatusReq( void * pBuffer, u16 buffSize, u16 transactionID ) { if (pBuffer == 0 || buffSize < QMIWDSGetPKGSRVCStatusReqSize() ) { return -ENOMEM; } // QMI WDS Get PKG SRVC Status REQ // Request *(u8 *)(pBuffer + sizeof( sQMUX )) = 0x00; // Transaction ID put_unaligned(cpu_to_le16(transactionID), (u16 *)(pBuffer + sizeof( sQMUX ) + 1)); // Message ID put_unaligned(cpu_to_le16(0x0022), (u16 *)(pBuffer + sizeof( sQMUX ) + 3)); // Size of TLV's put_unaligned(cpu_to_le16(0x0000), (u16 *)(pBuffer + sizeof( sQMUX ) + 5)); // success return sizeof( sQMUX ) + 7; } #if 0 static u16 QMIWDSSetQMUXBindMuxDataPortSize( void ) { return sizeof( sQMUX ) + 29; } static u16 QMIWDSSetQMUXBindMuxDataPortReq( void * pBuffer, u16 buffSize, u8 MuxId, u16 transactionID ) { if (pBuffer == 0 || buffSize < QMIWDSSetQMUXBindMuxDataPortSize() ) { return -ENOMEM; } // QMI WDS Set QMUX Bind Mux Data Port REQ // Request *(u8 *)(pBuffer + sizeof( sQMUX )) = 0x00; // Transaction ID put_unaligned(cpu_to_le16(transactionID), (u16 *)(pBuffer + sizeof( sQMUX ) + 1)); // Message ID put_unaligned(cpu_to_le16(0x00a2), (u16 *)(pBuffer + sizeof( sQMUX ) + 3)); // Size of TLV's put_unaligned(cpu_to_le16(0x0016), (u16 *)(pBuffer + sizeof( sQMUX ) + 5)); *(u8 *)(pBuffer + sizeof( sQMUX ) + 7) = 0x10; put_unaligned(cpu_to_le16(0x08), (u16 *)(pBuffer + sizeof( sQMUX ) + 8)); put_unaligned(cpu_to_le32(0x02), (u32 *)(pBuffer + sizeof( sQMUX ) + 10)); // ep_type put_unaligned(cpu_to_le32(0x04), (u32 *)(pBuffer + sizeof( sQMUX ) + 14)); // iface_id *(u8 *)(pBuffer + sizeof( sQMUX ) + 18) = 0x11; put_unaligned(cpu_to_le16(0x01), (u16 *)(pBuffer + sizeof( sQMUX ) + 19)); *(u8 *)(pBuffer + sizeof( sQMUX ) + 21) = MuxId; // MuxId *(u8 *)(pBuffer + sizeof( sQMUX ) + 22) = 0x13; put_unaligned(cpu_to_le16(0x04), (u16 *)(pBuffer + sizeof( sQMUX ) + 23)); put_unaligned(cpu_to_le32(0x01), (u32 *)(pBuffer + sizeof( sQMUX ) + 25)); // success return sizeof( sQMUX ) + 29; } #endif /*=========================================================================== METHOD: QMIDMSGetMEIDReq (Public Method) DESCRIPTION: Fill buffer with QMI DMS Get Serial Numbers Request PARAMETERS pBuffer [ 0 ] - Buffer to be filled buffSize [ I ] - Size of pBuffer transactionID [ I ] - Transaction ID RETURN VALUE: int - Positive for resulting size of pBuffer Negative errno for error ===========================================================================*/ static int QMIDMSGetMEIDReq( void * pBuffer, u16 buffSize, u16 transactionID ) { if (pBuffer == 0 || buffSize < QMIDMSGetMEIDReqSize() ) { return -ENOMEM; } // QMI DMS GET SERIAL NUMBERS REQ // Request *(u8 *)(pBuffer + sizeof( sQMUX )) = 0x00; // Transaction ID put_unaligned( cpu_to_le16(transactionID), (u16 *)(pBuffer + sizeof( sQMUX ) + 1) ); // Message ID put_unaligned( cpu_to_le16(0x0025), (u16 *)(pBuffer + sizeof( sQMUX ) + 3) ); // Size of TLV's put_unaligned( cpu_to_le16(0x0000), (u16 *)(pBuffer + sizeof( sQMUX ) + 5)); // success return sizeof( sQMUX ) + 7; } /*=========================================================================== METHOD: QMIWDASetDataFormatReq (Public Method) DESCRIPTION: Fill buffer with QMI WDA Set Data Format Request PARAMETERS pBuffer [ 0 ] - Buffer to be filled buffSize [ I ] - Size of pBuffer transactionID [ I ] - Transaction ID RETURN VALUE: int - Positive for resulting size of pBuffer Negative errno for error ===========================================================================*/ static int QMIWDASetDataFormatReq( void * pBuffer, u16 buffSize, bool bRawIPMode, int qmap_mode, u32 rx_size, u16 transactionID ) { if (qmap_mode) { struct QMIWDS_ADMIN_SET_DATA_FORMAT_REQ_MSG *pMUXMsg = (struct QMIWDS_ADMIN_SET_DATA_FORMAT_REQ_MSG *)(pBuffer + sizeof( sQMUX )); pMUXMsg->CtlFlags = 0x00; put_unaligned( cpu_to_le16(transactionID), &pMUXMsg->TransactionId); put_unaligned( cpu_to_le16(0x0020), &pMUXMsg->Type); put_unaligned( cpu_to_le16(sizeof( struct QMIWDS_ADMIN_SET_DATA_FORMAT_REQ_MSG) - 7), &pMUXMsg->Length); //Indicates whether the Quality of Service(QOS) data format is used by the client. pMUXMsg->QosDataFormatTlv.TLVType = 0x10; pMUXMsg->QosDataFormatTlv.TLVLength = cpu_to_le16(0x0001); pMUXMsg->QosDataFormatTlv.QOSSetting = 0; /* no-QOS header */ //Underlying Link Layer Protocol pMUXMsg->UnderlyingLinkLayerProtocolTlv.TLVType = 0x11; pMUXMsg->UnderlyingLinkLayerProtocolTlv.TLVLength = cpu_to_le16(4); pMUXMsg->UnderlyingLinkLayerProtocolTlv.Value = cpu_to_le32(0x02); /* Set Ethernet mode */ //Uplink (UL) data aggregation protocol to be used for uplink data transfer. pMUXMsg->UplinkDataAggregationProtocolTlv.TLVType = 0x12; pMUXMsg->UplinkDataAggregationProtocolTlv.TLVLength = cpu_to_le16(4); pMUXMsg->UplinkDataAggregationProtocolTlv.Value = cpu_to_le32(0x05); //UL QMAP is enabled //Downlink (DL) data aggregation protocol to be used for downlink data transfer pMUXMsg->DownlinkDataAggregationProtocolTlv.TLVType = 0x13; pMUXMsg->DownlinkDataAggregationProtocolTlv.TLVLength = cpu_to_le16(4); pMUXMsg->DownlinkDataAggregationProtocolTlv.Value = cpu_to_le32(0x05); //UL QMAP is enabled //Maximum number of datagrams in a single aggregated packet on downlink pMUXMsg->DownlinkDataAggregationMaxDatagramsTlv.TLVType = 0x15; pMUXMsg->DownlinkDataAggregationMaxDatagramsTlv.TLVLength = cpu_to_le16(4); pMUXMsg->DownlinkDataAggregationMaxDatagramsTlv.Value = cpu_to_le32(rx_size/1024); //Maximum size in bytes of a single aggregated packet allowed on downlink pMUXMsg->DownlinkDataAggregationMaxSizeTlv.TLVType = 0x16; pMUXMsg->DownlinkDataAggregationMaxSizeTlv.TLVLength = cpu_to_le16(4); pMUXMsg->DownlinkDataAggregationMaxSizeTlv.Value = cpu_to_le32(rx_size); //Peripheral End Point ID pMUXMsg->epTlv.TLVType = 0x17; pMUXMsg->epTlv.TLVLength = cpu_to_le16(8); pMUXMsg->epTlv.ep_type = cpu_to_le32(0x02); // DATA_EP_TYPE_BAM_DMUX pMUXMsg->epTlv.iface_id = cpu_to_le32(0x04); #if 0 //Specifies the minimum padding bytes to be added in between aggregated downlink QMAP packets. pMUXMsg->dl_minimum_padding.TLVType = 0x19; pMUXMsg->dl_minimum_padding.TLVLength = cpu_to_le16(4); pMUXMsg->dl_minimum_padding.Value = cpu_to_le32(0); #endif } else { if (pBuffer == 0 || buffSize < QMIWDASetDataFormatReqSize(qmap_mode) ) { return -ENOMEM; } // QMI WDA SET DATA FORMAT REQ // Request *(u8 *)(pBuffer + sizeof( sQMUX )) = 0x00; // Transaction ID put_unaligned( cpu_to_le16(transactionID), (u16 *)(pBuffer + sizeof( sQMUX ) + 1) ); // Message ID put_unaligned( cpu_to_le16(0x0020), (u16 *)(pBuffer + sizeof( sQMUX ) + 3) ); // Size of TLV's put_unaligned( cpu_to_le16(0x000b), (u16 *)(pBuffer + sizeof( sQMUX ) + 5)); /* TLVType QOS Data Format 1 byte */ *(u8 *)(pBuffer + sizeof( sQMUX ) + 7) = 0x10; // type data format /* TLVLength 2 bytes - see spec */ put_unaligned( cpu_to_le16(0x0001), (u16 *)(pBuffer + sizeof( sQMUX ) + 8)); /* DataFormat: 0-default; 1-QoS hdr present 2 bytes */ #ifdef QOS_MODE *(u8 *)(pBuffer + sizeof( sQMUX ) + 10) = 1; /* QOS header */ #else *(u8 *)(pBuffer + sizeof( sQMUX ) + 10) = 0; /* no-QOS header */ #endif /* TLVType Link-Layer Protocol (Optional) 1 byte */ *(u8 *)(pBuffer + sizeof( sQMUX ) + 11) = 0x11; /* TLVLength 2 bytes */ put_unaligned( cpu_to_le16(0x0004), (u16 *)(pBuffer + sizeof( sQMUX ) + 12)); /* LinkProt: 0x1 - ETH; 0x2 - rawIP 4 bytes */ if (bRawIPMode) { //#ifdef DATA_MODE_RP /* Set RawIP mode */ put_unaligned( cpu_to_le32(0x00000002), (u32 *)(pBuffer + sizeof( sQMUX ) + 14)); DBG("Request RawIP Data Format\n"); } else { //#else /* Set Ethernet mode */ put_unaligned( cpu_to_le32(0x00000001), (u32 *)(pBuffer + sizeof( sQMUX ) + 14)); DBG("Request Ethernet Data Format\n"); } //#endif } // success return QMIWDASetDataFormatReqSize(qmap_mode); } #if 0 static int QMIWDASetDataQmapReq( void * pBuffer, u16 buffSize, u16 transactionID ) { // QMI WDA SET DATA FORMAT REQ // Request *(u8 *)(pBuffer + sizeof( sQMUX )) = 0x00; // Transaction ID put_unaligned( cpu_to_le16(transactionID), (u16 *)(pBuffer + sizeof( sQMUX ) + 1) ); // Message ID put_unaligned( cpu_to_le16(0x002B), (u16 *)(pBuffer + sizeof( sQMUX ) + 3) ); // Size of TLV's put_unaligned( cpu_to_le16(0x0004), (u16 *)(pBuffer + sizeof( sQMUX ) + 5)); /* TLVType QMAP In-Band Flow Control 1 byte */ *(u8 *)(pBuffer + sizeof( sQMUX ) + 7) = 0x10; put_unaligned( cpu_to_le16(0x0001), (u16 *)(pBuffer + sizeof( sQMUX ) + 8)); *(u8 *)(pBuffer + sizeof( sQMUX ) + 10) = 0x01; // success return ( sizeof( sQMUX ) + 11); } #endif #if 0 /*=========================================================================== METHOD: QMICTLSetDataFormatReqSize (Public Method) DESCRIPTION: Get size of buffer needed for QMUX + QMICTLSetDataFormatReq RETURN VALUE: u16 - size of buffer ===========================================================================*/ static u16 QMICTLSetDataFormatReqSize( void ) { return sizeof( sQMUX ) + 15; } /*=========================================================================== METHOD: QMICTLSetDataFormatReq (Public Method) DESCRIPTION: Fill buffer with QMI CTL Set Data Format Request PARAMETERS pBuffer [ 0 ] - Buffer to be filled buffSize [ I ] - Size of pBuffer transactionID [ I ] - Transaction ID RETURN VALUE: int - Positive for resulting size of pBuffer Negative errno for error ===========================================================================*/ static int QMICTLSetDataFormatReq( void * pBuffer, u16 buffSize, u8 transactionID ) { if (pBuffer == 0 || buffSize < QMICTLSetDataFormatReqSize() ) { return -ENOMEM; } /* QMI CTL Set Data Format Request */ /* Request */ *(u8 *)(pBuffer + sizeof( sQMUX )) = 0x00; // QMICTL_FLAG_REQUEST /* Transaction ID 1 byte */ *(u8 *)(pBuffer + sizeof( sQMUX ) + 1) = transactionID; /* 1 byte as in spec */ /* QMICTLType 2 bytes */ put_unaligned( cpu_to_le16(0x0026), (u16 *)(pBuffer + sizeof( sQMUX ) + 2)); /* Length 2 bytes of 2 TLVs each - see spec */ put_unaligned( cpu_to_le16(0x0009), (u16 *)(pBuffer + sizeof( sQMUX ) + 4)); /* TLVType Data Format (Mandatory) 1 byte */ *(u8 *)(pBuffer + sizeof( sQMUX ) + 6) = 0x01; // type data format /* TLVLength 2 bytes - see spec */ put_unaligned( cpu_to_le16(0x0001), (u16 *)(pBuffer + sizeof( sQMUX ) + 7)); /* DataFormat: 0-default; 1-QoS hdr present 2 bytes */ #ifdef QOS_MODE *(u8 *)(pBuffer + sizeof( sQMUX ) + 9) = 1; /* QOS header */ #else *(u8 *)(pBuffer + sizeof( sQMUX ) + 9) = 0; /* no-QOS header */ #endif /* TLVType Link-Layer Protocol (Optional) 1 byte */ *(u8 *)(pBuffer + sizeof( sQMUX ) + 10) = TLV_TYPE_LINK_PROTO; /* TLVLength 2 bytes */ put_unaligned( cpu_to_le16(0x0002), (u16 *)(pBuffer + sizeof( sQMUX ) + 11)); /* LinkProt: 0x1 - ETH; 0x2 - rawIP 2 bytes */ #ifdef DATA_MODE_RP /* Set RawIP mode */ put_unaligned( cpu_to_le16(0x0002), (u16 *)(pBuffer + sizeof( sQMUX ) + 13)); DBG("Request RawIP Data Format\n"); #else /* Set Ethernet mode */ put_unaligned( cpu_to_le16(0x0001), (u16 *)(pBuffer + sizeof( sQMUX ) + 13)); DBG("Request Ethernet Data Format\n"); #endif /* success */ return sizeof( sQMUX ) + 15; } #endif /*=========================================================================== METHOD: QMICTLSyncReq (Public Method) DESCRIPTION: Fill buffer with QMI CTL Sync Request PARAMETERS pBuffer [ 0 ] - Buffer to be filled buffSize [ I ] - Size of pBuffer transactionID [ I ] - Transaction ID RETURN VALUE: int - Positive for resulting size of pBuffer Negative errno for error ===========================================================================*/ static int QMICTLSyncReq( void * pBuffer, u16 buffSize, u16 transactionID ) { if (pBuffer == 0 || buffSize < QMICTLSyncReqSize() ) { return -ENOMEM; } // Request *(u8 *)(pBuffer + sizeof( sQMUX )) = 0x00; // Transaction ID *(u8 *)(pBuffer + sizeof( sQMUX ) + 1) = transactionID; // Message ID put_unaligned( cpu_to_le16(0x0027), (u16 *)(pBuffer + sizeof( sQMUX ) + 2) ); // Size of TLV's put_unaligned( cpu_to_le16(0x0000), (u16 *)(pBuffer + sizeof( sQMUX ) + 4) ); // success return sizeof( sQMUX ) + 6; } /*=========================================================================*/ // Parse data from QMI responses /*=========================================================================*/ /*=========================================================================== METHOD: QMICTLGetClientIDResp (Public Method) DESCRIPTION: Parse the QMI CTL Get Client ID Resp PARAMETERS pBuffer [ I ] - Buffer to be parsed buffSize [ I ] - Size of pBuffer pClientID [ 0 ] - Recieved client ID RETURN VALUE: int - 0 for success Negative errno for error ===========================================================================*/ static int QMICTLGetClientIDResp( void * pBuffer, u16 buffSize, u16 * pClientID ) { int result; // Ignore QMUX and SDU // QMI CTL SDU is 2 bytes, not 3 u8 offset = sizeof( sQMUX ) + 2; if (pBuffer == 0 || buffSize < offset) { return -ENOMEM; } pBuffer = pBuffer + offset; buffSize -= offset; result = GetQMIMessageID( pBuffer, buffSize ); if (result != 0x22) { return -EFAULT; } result = ValidQMIMessage( pBuffer, buffSize ); if (result != 0) { return -EFAULT; } result = GetTLV( pBuffer, buffSize, 0x01, pClientID, 2 ); if (result != 2) { return -EFAULT; } return 0; } /*=========================================================================== METHOD: QMICTLReleaseClientIDResp (Public Method) DESCRIPTION: Verify the QMI CTL Release Client ID Resp is valid PARAMETERS pBuffer [ I ] - Buffer to be parsed buffSize [ I ] - Size of pBuffer RETURN VALUE: int - 0 for success Negative errno for error ===========================================================================*/ static int QMICTLReleaseClientIDResp( void * pBuffer, u16 buffSize ) { int result; // Ignore QMUX and SDU // QMI CTL SDU is 2 bytes, not 3 u8 offset = sizeof( sQMUX ) + 2; if (pBuffer == 0 || buffSize < offset) { return -ENOMEM; } pBuffer = pBuffer + offset; buffSize -= offset; result = GetQMIMessageID( pBuffer, buffSize ); if (result != 0x23) { return -EFAULT; } result = ValidQMIMessage( pBuffer, buffSize ); if (result != 0) { return -EFAULT; } return 0; } /*=========================================================================== METHOD: QMIWDSEventResp (Public Method) DESCRIPTION: Parse the QMI WDS Set Event Report Resp/Indication or QMI WDS Get PKG SRVC Status Resp/Indication Return parameters will only be updated if value was received PARAMETERS pBuffer [ I ] - Buffer to be parsed buffSize [ I ] - Size of pBuffer pTXOk [ O ] - Number of transmitted packets without errors pRXOk [ O ] - Number of recieved packets without errors pTXErr [ O ] - Number of transmitted packets with framing errors pRXErr [ O ] - Number of recieved packets with framing errors pTXOfl [ O ] - Number of transmitted packets dropped due to overflow pRXOfl [ O ] - Number of recieved packets dropped due to overflow pTXBytesOk [ O ] - Number of transmitted bytes without errors pRXBytesOk [ O ] - Number of recieved bytes without errors pbLinkState [ 0 ] - Is the link active? pbReconfigure [ 0 ] - Must interface be reconfigured? (reset IP address) RETURN VALUE: int - 0 for success Negative errno for error ===========================================================================*/ static int QMIWDSEventResp( void * pBuffer, u16 buffSize, u32 * pTXOk, u32 * pRXOk, u32 * pTXErr, u32 * pRXErr, u32 * pTXOfl, u32 * pRXOfl, u64 * pTXBytesOk, u64 * pRXBytesOk, bool * pbLinkState, bool * pbReconfigure ) { int result; u8 pktStatusRead[2]; // Ignore QMUX and SDU u8 offset = sizeof( sQMUX ) + 3; if (pBuffer == 0 || buffSize < offset || pTXOk == 0 || pRXOk == 0 || pTXErr == 0 || pRXErr == 0 || pTXOfl == 0 || pRXOfl == 0 || pTXBytesOk == 0 || pRXBytesOk == 0 || pbLinkState == 0 || pbReconfigure == 0 ) { return -ENOMEM; } pBuffer = pBuffer + offset; buffSize -= offset; // Note: Indications. No Mandatory TLV required result = GetQMIMessageID( pBuffer, buffSize ); // QMI WDS Set Event Report Resp if (result == 0x01) { // TLV's are not mandatory GetTLV( pBuffer, buffSize, 0x10, (void*)pTXOk, 4 ); put_unaligned( le32_to_cpu(*pTXOk), pTXOk); GetTLV( pBuffer, buffSize, 0x11, (void*)pRXOk, 4 ); put_unaligned( le32_to_cpu(*pRXOk), pRXOk); GetTLV( pBuffer, buffSize, 0x12, (void*)pTXErr, 4 ); put_unaligned( le32_to_cpu(*pTXErr), pTXErr); GetTLV( pBuffer, buffSize, 0x13, (void*)pRXErr, 4 ); put_unaligned( le32_to_cpu(*pRXErr), pRXErr); GetTLV( pBuffer, buffSize, 0x14, (void*)pTXOfl, 4 ); put_unaligned( le32_to_cpu(*pTXOfl), pTXOfl); GetTLV( pBuffer, buffSize, 0x15, (void*)pRXOfl, 4 ); put_unaligned( le32_to_cpu(*pRXOfl), pRXOfl); GetTLV( pBuffer, buffSize, 0x19, (void*)pTXBytesOk, 8 ); put_unaligned( le64_to_cpu(*pTXBytesOk), pTXBytesOk); GetTLV( pBuffer, buffSize, 0x1A, (void*)pRXBytesOk, 8 ); put_unaligned( le64_to_cpu(*pRXBytesOk), pRXBytesOk); } // QMI WDS Get PKG SRVC Status Resp else if (result == 0x22) { result = GetTLV( pBuffer, buffSize, 0x01, &pktStatusRead[0], 2 ); // 1 or 2 bytes may be received if (result >= 1) { if (pktStatusRead[0] == 0x02) { *pbLinkState = true; } else { *pbLinkState = false; } } if (result == 2) { if (pktStatusRead[1] == 0x01) { *pbReconfigure = true; } else { *pbReconfigure = false; } } if (result < 0) { return result; } } else { return -EFAULT; } return 0; } /*=========================================================================== METHOD: QMIDMSGetMEIDResp (Public Method) DESCRIPTION: Parse the QMI DMS Get Serial Numbers Resp PARAMETERS pBuffer [ I ] - Buffer to be parsed buffSize [ I ] - Size of pBuffer pMEID [ O ] - Device MEID meidSize [ I ] - Size of MEID buffer (at least 14) RETURN VALUE: int - 0 for success Negative errno for error ===========================================================================*/ static int QMIDMSGetMEIDResp( void * pBuffer, u16 buffSize, char * pMEID, int meidSize ) { int result; // Ignore QMUX and SDU u8 offset = sizeof( sQMUX ) + 3; if (pBuffer == 0 || buffSize < offset || meidSize < 14) { return -ENOMEM; } pBuffer = pBuffer + offset; buffSize -= offset; result = GetQMIMessageID( pBuffer, buffSize ); if (result != 0x25) { return -EFAULT; } result = ValidQMIMessage( pBuffer, buffSize ); if (result != 0) { return -EFAULT; } result = GetTLV( pBuffer, buffSize, 0x12, (void*)pMEID, 14 ); if (result != 14) { return -EFAULT; } return 0; } /*=========================================================================== METHOD: QMIWDASetDataFormatResp (Public Method) DESCRIPTION: Parse the QMI WDA Set Data Format Response PARAMETERS pBuffer [ I ] - Buffer to be parsed buffSize [ I ] - Size of pBuffer RETURN VALUE: int - 0 for success Negative errno for error ===========================================================================*/ static int QMIWDASetDataFormatResp( void * pBuffer, u16 buffSize, bool bRawIPMode, int *qmap_enabled, int *rx_size, int *tx_size) { int result; u8 pktLinkProtocol[4]; // Ignore QMUX and SDU // QMI SDU is 3 bytes u8 offset = sizeof( sQMUX ) + 3; if (pBuffer == 0 || buffSize < offset) { return -ENOMEM; } pBuffer = pBuffer + offset; buffSize -= offset; result = GetQMIMessageID( pBuffer, buffSize ); if (result != 0x20) { return -EFAULT; } /* Check response message result TLV */ result = ValidQMIMessage( pBuffer, buffSize ); if (result != 0) { DBG("EFAULT: Data Format Mode Bad Response\n"); // return -EFAULT; return 0; } /* Check response message link protocol */ result = GetTLV( pBuffer, buffSize, 0x11, &pktLinkProtocol[0], 4); if (result != 4) { DBG("EFAULT: Wrong TLV format\n"); return 0; } if (bRawIPMode) { ////#ifdef DATA_MODE_RP if (pktLinkProtocol[0] != 2) { DBG("EFAULT: Data Format Cannot be set to RawIP Mode\n"); return pktLinkProtocol[0]; } DBG("Data Format Set to RawIP\n"); } else { ////#else if (pktLinkProtocol[0] != 1) { DBG("EFAULT: Data Format Cannot be set to Ethernet Mode\n"); return pktLinkProtocol[0]; } DBG("Data Format Set to Ethernet Mode \n"); } //#endif GetTLV( pBuffer, buffSize, 0x12, qmap_enabled, 4); if (le32_to_cpu(*qmap_enabled) == 5) GetTLV( pBuffer, buffSize, 0x13, qmap_enabled, 4); GetTLV( pBuffer, buffSize, 0x16, rx_size, 4); GetTLV( pBuffer, buffSize, 0x18, tx_size, 4); return pktLinkProtocol[0]; } /*=========================================================================== METHOD: QMICTLSyncResp (Public Method) DESCRIPTION: Validate the QMI CTL Sync Response PARAMETERS pBuffer [ I ] - Buffer to be parsed buffSize [ I ] - Size of pBuffer RETURN VALUE: int - 0 for success Negative errno for error ===========================================================================*/ static int QMICTLSyncResp( void *pBuffer, u16 buffSize ) { int result; // Ignore QMUX (2 bytes for QMI CTL) and SDU u8 offset = sizeof( sQMUX ) + 2; if (pBuffer == 0 || buffSize < offset) { return -ENOMEM; } pBuffer = pBuffer + offset; buffSize -= offset; result = GetQMIMessageID( pBuffer, buffSize ); if (result != 0x27) { return -EFAULT; } result = ValidQMIMessage( pBuffer, buffSize ); return result; } #endif