初始化仓库

This commit is contained in:
ling 2023-03-31 20:15:54 +08:00
commit e65679b443
258 changed files with 111126 additions and 0 deletions

25
fibocom-dial/Makefile Normal file
View File

@ -0,0 +1,25 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=fibocom-dial
PKG_RELEASE:=1
include $(INCLUDE_DIR)/package.mk
define Package/fibocom-dial
SECTION:=utils
CATEGORY:=Utilities
TITLE:=Fibocom Dial
endef
define Package/fibocom-dial/description
Fibocom Dial (Prints a snarky message)
endef
define Package/fibocom-dial/install
$(INSTALL_DIR) $(1)/bin
$(INSTALL_BIN) $(PKG_BUILD_DIR)/fibocom-dial $(1)/bin/
$(INSTALL_BIN) $(PKG_BUILD_DIR)/fibo_qmimsg_server $(1)/bin/
$(INSTALL_BIN) $(PKG_BUILD_DIR)/multi-pdn-manager $(1)/bin/
endef
$(eval $(call BuildPackage,fibocom-dial))

View File

@ -0,0 +1,275 @@
#include <ctype.h>
#include <stdio.h>
#include <string.h>
#include <termios.h>
#include "QMIThread.h"
#ifdef CONFIG_GOBINET
// IOCTL to generate a client ID for this service type
#define IOCTL_QMI_GET_SERVICE_FILE 0x8BE0 + 1
// IOCTL to get the VIDPID of the device
#define IOCTL_QMI_GET_DEVICE_VIDPID 0x8BE0 + 2
// IOCTL to get the MEID of the device
#define IOCTL_QMI_GET_DEVICE_MEID 0x8BE0 + 3
static int GobiNetSendQMI(PQCQMIMSG pRequest)
{
int ret, fd;
static int send_count = 0;
fd = qmiclientId[pRequest->QMIHdr.QMIType];
if (fd <= 0) {
dbg_time("%s QMIType: %d has no clientID", __func__,
pRequest->QMIHdr.QMIType);
return -ENODEV;
}
// Always ready to write
re_write:
if (1 == 1) {
ssize_t nwrites =
le16_to_cpu(pRequest->QMIHdr.Length) + 1 - sizeof(QCQMI_HDR);
ret = write(fd, &pRequest->MUXMsg, nwrites);
if (ret == nwrites)
{
ret = 0;
send_count = 0;
}
else
{
send_count++;
dbg_time("%s write=%d, errno: %d (%s) send_count %d", __func__, ret, errno, strerror(errno), send_count);
if (send_count < 3)
{
sleep(1);
goto re_write;
}
}
} else {
dbg_time("%s poll=%d, errno: %d (%s)", __func__, ret, errno,
strerror(errno));
}
return ret;
}
static int GobiNetGetClientID(const char *qcqmi, UCHAR QMIType)
{
int ClientId;
ClientId = open(qcqmi, O_RDWR | O_NONBLOCK | O_NOCTTY);
if (ClientId == -1) {
dbg_time("failed to open %s, errno: %d (%s)", qcqmi, errno,
strerror(errno));
return -1;
}
if (ioctl(ClientId, IOCTL_QMI_GET_SERVICE_FILE, QMIType) != 0) {
dbg_time("failed to get ClientID for 0x%02x errno: %d (%s)", QMIType,
errno, strerror(errno));
close(ClientId);
ClientId = 0;
}
dbg_time("%s: QMIType = %d clientid %d", __func__, QMIType, ClientId);
switch (QMIType) {
case QMUX_TYPE_WDS:
dbg_time("Get clientWDS = %d", ClientId);
break;
case QMUX_TYPE_DMS:
dbg_time("Get clientDMS = %d", ClientId);
break;
case QMUX_TYPE_NAS:
dbg_time("Get clientNAS = %d", ClientId);
break;
case QMUX_TYPE_QOS:
dbg_time("Get clientQOS = %d", ClientId);
break;
case QMUX_TYPE_WMS:
dbg_time("Get clientWMS = %d", ClientId);
break;
case QMUX_TYPE_PDS:
dbg_time("Get clientPDS = %d", ClientId);
break;
case QMUX_TYPE_UIM:
dbg_time("Get clientUIM = %d", ClientId);
break;
case QMUX_TYPE_WDS_ADMIN:
dbg_time("Get clientWDA = %d", ClientId);
break;
default:
break;
}
return ClientId;
}
static int GobiNetDeInit(void)
{
unsigned int i;
for (i = 0; i < sizeof(qmiclientId) / sizeof(qmiclientId[0]); i++) {
if (qmiclientId[i] != 0) {
close(qmiclientId[i]);
qmiclientId[i] = 0;
}
}
return 0;
}
static void *GobiNetThread(void *pData)
{
PROFILE_T *profile = (PROFILE_T *)pData;
const char *qcqmi = (const char *)profile->qmichannel;
int wait_for_request_quit = 0;
dbg_time("%s %d", __func__, __LINE__);
if (profile->ipv4_flag)
qmiclientId[QMUX_TYPE_WDS] = GobiNetGetClientID(qcqmi, QMUX_TYPE_WDS);
if (profile->ipv6_flag)
qmiclientId[QMUX_TYPE_WDS_IPV6] = GobiNetGetClientID(qcqmi, QMUX_TYPE_WDS);
qmiclientId[QMUX_TYPE_DMS] = GobiNetGetClientID(qcqmi, QMUX_TYPE_DMS);
qmiclientId[QMUX_TYPE_NAS] = GobiNetGetClientID(qcqmi, QMUX_TYPE_NAS);
qmiclientId[QMUX_TYPE_UIM] = GobiNetGetClientID(qcqmi, QMUX_TYPE_UIM);
// qmiclientId[QMUX_TYPE_WDS_ADMIN] =
// GobiNetGetClientID(qcqmi, QMUX_TYPE_WDS_ADMIN);
//if ((qmiclientId[QMUX_TYPE_WDS] == 0) && (qmiclientId[QMUX_TYPE_WDS_IPV6] == 0)) /*|| (clientWDA == -1)*/ {
if ((qmiclientId[QMUX_TYPE_DMS] == 0) ||
(qmiclientId[QMUX_TYPE_NAS] == 0) ||
(qmiclientId[QMUX_TYPE_UIM] == 0) ||
(profile->ipv4_flag ? ((qmiclientId[QMUX_TYPE_WDS] == 0) ? 1 : 0):0)||
(profile->ipv6_flag ? ((qmiclientId[QMUX_TYPE_WDS_IPV6] == 0) ? 1 : 0):0))
{
GobiNetDeInit();
dbg_time("%s Failed to open %s, errno: %d (%s)", __func__, qcqmi, errno,strerror(errno));
qmidevice_send_event_to_main(RIL_INDICATE_DEVICE_DISCONNECTED);
pthread_exit(NULL);
return NULL;
}
qmidevice_send_event_to_main(RIL_INDICATE_DEVICE_CONNECTED);
while (1) {
struct pollfd pollfds[16] = {{qmidevice_control_fd[1], POLLIN, 0}};
int ne, ret, nevents = 1;
unsigned int i;
for (i = 0; i < sizeof(qmiclientId) / sizeof(qmiclientId[0]); i++) {
if (qmiclientId[i] != 0) {
pollfds[nevents].fd = qmiclientId[i];
pollfds[nevents].events = POLLIN;
pollfds[nevents].revents = 0;
nevents++;
}
}
do {
ret = poll(pollfds, nevents, wait_for_request_quit ? 1000 : -1);
} while ((ret < 0) && (errno == EINTR));
if (ret == 0 && wait_for_request_quit) {
QmiThreadRecvQMI(
NULL); // main thread may pending on QmiThreadSendQMI()
continue;
}
if (ret <= 0) {
dbg_time("%s poll=%d, errno: %d (%s)", __func__, ret, errno,
strerror(errno));
break;
}
for (ne = 0; ne < nevents; ne++) {
int fd = pollfds[ne].fd;
short revents = pollfds[ne].revents;
if (revents & (POLLERR | POLLHUP | POLLNVAL)) {
dbg_time("%s poll err/hup/inval", __func__);
dbg_time("epoll fd = %d, events = 0x%04x", fd, revents);
if (fd == qmidevice_control_fd[1]) {
} else {
}
if (revents & (POLLERR | POLLHUP | POLLNVAL))
goto __GobiNetThread_quit;
}
if ((revents & POLLIN) == 0)
continue;
if (fd == qmidevice_control_fd[1]) {
int triger_event;
if (read(fd, &triger_event, sizeof(triger_event)) ==
sizeof(triger_event)) {
// DBG("triger_event = 0x%x", triger_event);
switch (triger_event) {
case RIL_REQUEST_QUIT:
goto __GobiNetThread_quit;
break;
case SIGTERM:
wait_for_request_quit = 1;
break;
default:
break;
}
}
continue;
}
{
ssize_t nreads;
static UCHAR QMIBuf[4096];
PQCQMIMSG pResponse = (PQCQMIMSG)QMIBuf;
nreads = read(fd, &pResponse->MUXMsg,
sizeof(QMIBuf) - sizeof(QCQMI_HDR));
if (nreads <= 0) {
dbg_time("%s read=%d errno: %d (%s)", __func__, (int)nreads,
errno, strerror(errno));
break;
}
for (i = 0; i < sizeof(qmiclientId) / sizeof(qmiclientId[0]);
i++) {
if (qmiclientId[i] == fd) {
pResponse->QMIHdr.QMIType = i;
}
}
pResponse->QMIHdr.IFType = USB_CTL_MSG_TYPE_QMI;
pResponse->QMIHdr.Length =
cpu_to_le16(nreads + sizeof(QCQMI_HDR) - 1);
pResponse->QMIHdr.CtlFlags = 0x00;
pResponse->QMIHdr.ClientId = fd & 0xFF;
QmiThreadRecvQMI(pResponse);
}
}
}
__GobiNetThread_quit:
GobiNetDeInit();
qmidevice_send_event_to_main(RIL_INDICATE_DEVICE_DISCONNECTED);
QmiThreadRecvQMI(NULL); // main thread may pending on QmiThreadSendQMI()
dbg_time("%s exit", __func__);
pthread_exit(NULL);
return NULL;
}
#else
static int GobiNetSendQMI(PQCQMIMSG pRequest) { return -1; }
static void *GobiNetThread(void *pData)
{
dbg_time("please set CONFIG_GOBINET");
return NULL;
}
#endif
const struct qmi_device_ops gobi_qmidev_ops = {
.deinit = GobiNetDeInit,
.send = GobiNetSendQMI,
.read = GobiNetThread,
};

363
fibocom-dial/src/MPQCTL.h Normal file
View File

@ -0,0 +1,363 @@
/*===========================================================================
M P Q C T L. H
DESCRIPTION:
This module contains QMI QCTL module.
INITIALIZATION AND SEQUENCING REQUIREMENTS:
Copyright (C) 2011 by Qualcomm Technologies, Incorporated. All Rights Reserved.
===========================================================================*/
#ifndef MPQCTL_H
#define MPQCTL_H
#include "MPQMI.h"
#pragma pack(push, 1)
// ================= QMICTL ==================
// QMICTL Control Flags
#define QMICTL_CTL_FLAG_CMD 0x00
#define QMICTL_CTL_FLAG_RSP 0x01
#define QMICTL_CTL_FLAG_IND 0x02
#if 0
typedef struct _QMICTL_TRANSACTION_ITEM
{
LIST_ENTRY List;
UCHAR TransactionId; // QMICTL transaction id
PVOID Context; // Adapter or IocDev
PIRP Irp;
} QMICTL_TRANSACTION_ITEM, *PQMICTL_TRANSACTION_ITEM;
#endif
typedef struct _QCQMICTL_MSG_HDR {
UCHAR CtlFlags; // 00-cmd, 01-rsp, 10-ind
UCHAR TransactionId;
USHORT QMICTLType;
USHORT Length;
} __attribute__((packed)) QCQMICTL_MSG_HDR, *PQCQMICTL_MSG_HDR;
#define QCQMICTL_MSG_HDR_SIZE sizeof(QCQMICTL_MSG_HDR)
typedef struct _QCQMICTL_MSG_HDR_RESP {
UCHAR CtlFlags; // 00-cmd, 01-rsp, 10-ind
UCHAR TransactionId;
USHORT QMICTLType;
USHORT Length;
UCHAR TLVType; // 0x02 - result code
USHORT TLVLength; // 4
USHORT QMUXResult; // QMI_RESULT_SUCCESS
// QMI_RESULT_FAILURE
USHORT QMUXError; // QMI_ERR_INVALID_ARG
// QMI_ERR_NO_MEMORY
// QMI_ERR_INTERNAL
// QMI_ERR_FAULT
} __attribute__((packed)) QCQMICTL_MSG_HDR_RESP, *PQCQMICTL_MSG_HDR_RESP;
typedef struct _QCQMICTL_MSG {
UCHAR CtlFlags; // 00-cmd, 01-rsp, 10-ind
UCHAR TransactionId;
USHORT QMICTLType;
USHORT Length;
UCHAR Payload;
} __attribute__((packed)) QCQMICTL_MSG, *PQCQMICTL_MSG;
// TLV Header
typedef struct _QCQMICTL_TLV_HDR {
UCHAR TLVType;
USHORT TLVLength;
} __attribute__((packed)) QCQMICTL_TLV_HDR, *PQCQMICTL_TLV_HDR;
#define QCQMICTL_TLV_HDR_SIZE sizeof(QCQMICTL_TLV_HDR)
// QMICTL Type
#define QMICTL_SET_INSTANCE_ID_REQ 0x0020
#define QMICTL_SET_INSTANCE_ID_RESP 0x0020
#define QMICTL_GET_VERSION_REQ 0x0021
#define QMICTL_GET_VERSION_RESP 0x0021
#define QMICTL_GET_CLIENT_ID_REQ 0x0022
#define QMICTL_GET_CLIENT_ID_RESP 0x0022
#define QMICTL_RELEASE_CLIENT_ID_REQ 0x0023
#define QMICTL_RELEASE_CLIENT_ID_RESP 0x0023
#define QMICTL_REVOKE_CLIENT_ID_IND 0x0024
#define QMICTL_INVALID_CLIENT_ID_IND 0x0025
#define QMICTL_SET_DATA_FORMAT_REQ 0x0026
#define QMICTL_SET_DATA_FORMAT_RESP 0x0026
#define QMICTL_SYNC_REQ 0x0027
#define QMICTL_SYNC_RESP 0x0027
#define QMICTL_SYNC_IND 0x0027
#define QMICTL_FLAG_REQUEST 0x00
#define QMICTL_FLAG_RESPONSE 0x01
#define QMICTL_FLAG_INDICATION 0x02
// QMICTL Message Definitions
typedef struct _QMICTL_SET_INSTANCE_ID_REQ_MSG {
UCHAR CtlFlags; // QMICTL_FLAG_REQUEST
UCHAR TransactionId;
USHORT QMICTLType; // QMICTL_SET_INSTANCE_ID_REQ
USHORT Length; // 4
UCHAR TLVType; // QCTLV_TYPE_REQUIRED_PARAMETER
USHORT TLVLength; // 1
UCHAR Value; // Host-unique QMI instance for this device driver
} __attribute__((packed)) QMICTL_SET_INSTANCE_ID_REQ_MSG,
*PQMICTL_SET_INSTANCE_ID_REQ_MSG;
typedef struct _QMICTL_SET_INSTANCE_ID_RESP_MSG {
UCHAR CtlFlags; // QMICTL_FLAG_RESPONSE
UCHAR TransactionId;
USHORT QMICTLType; // QMICTL_SET_INSTANCE_ID_RESP
USHORT Length;
UCHAR TLVType; // QCTLV_TYPE_RESULT_CODE
USHORT TLVLength; // 0x0004
USHORT QMIResult;
USHORT QMIError;
UCHAR TLV2Type; // QCTLV_TYPE_REQUIRED_PARAMETER
USHORT TLV2Length; // 0x0002
USHORT QMI_ID; // Upper byte is assigned by MSM,
// lower assigned by host
} __attribute__((packed)) QMICTL_SET_INSTANCE_ID_RESP_MSG,
*PQMICTL_SET_INSTANCE_ID_RESP_MSG;
typedef struct _QMICTL_GET_VERSION_REQ_MSG {
UCHAR CtlFlags; // QMICTL_FLAG_REQUEST
UCHAR TransactionId;
USHORT QMICTLType; // QMICTL_GET_VERSION_REQ
USHORT Length; // 0
UCHAR TLVType; // QCTLV_TYPE_REQUIRED_PARAMETER
USHORT TLVLength; // var
UCHAR QMUXTypes; // List of one byte QMUX_TYPE values
// 0xFF returns a list of versions for all
// QMUX_TYPEs implemented on the device
} __attribute__((packed)) QMICTL_GET_VERSION_REQ_MSG,
*PQMICTL_GET_VERSION_REQ_MSG;
typedef struct _QMUX_TYPE_VERSION_STRUCT {
UCHAR QMUXType;
USHORT MajorVersion;
USHORT MinorVersion;
} __attribute__((packed)) QMUX_TYPE_VERSION_STRUCT, *PQMUX_TYPE_VERSION_STRUCT;
typedef struct _ADDENDUM_VERSION_PREAMBLE {
UCHAR LabelLength;
UCHAR Label;
} __attribute__((packed)) ADDENDUM_VERSION_PREAMBLE,
*PADDENDUM_VERSION_PREAMBLE;
#define QMICTL_GET_VERSION_RSP_TLV_TYPE_VERSION 0x01
#define QMICTL_GET_VERSION_RSP_TLV_TYPE_ADD_VERSION 0x10
typedef struct _QMICTL_GET_VERSION_RESP_MSG {
UCHAR CtlFlags; // QMICTL_FLAG_RESPONSE
UCHAR TransactionId;
USHORT QMICTLType; // QMICTL_GET_VERSION_RESP
USHORT Length;
UCHAR TLVType; // QCTLV_TYPE_RESULT_CODE
USHORT TLVLength; // 0x0004
USHORT QMIResult;
USHORT QMIError;
UCHAR TLV2Type; // QCTLV_TYPE_REQUIRED_PARAMETER
USHORT TLV2Length; // var
UCHAR NumElements; // Num of QMUX_TYPE_VERSION_STRUCT
QMUX_TYPE_VERSION_STRUCT TypeVersion[0];
} __attribute__((packed)) QMICTL_GET_VERSION_RESP_MSG,
*PQMICTL_GET_VERSION_RESP_MSG;
typedef struct _QMICTL_GET_CLIENT_ID_REQ_MSG {
UCHAR CtlFlags; // QMICTL_FLAG_REQUEST
UCHAR TransactionId;
USHORT QMICTLType; // QMICTL_GET_CLIENT_ID_REQ
USHORT Length;
UCHAR TLVType; // QCTLV_TYPE_REQUIRED_PARAMETER
USHORT TLVLength; // 1
UCHAR QMIType; // QMUX type
} __attribute__((packed)) QMICTL_GET_CLIENT_ID_REQ_MSG,
*PQMICTL_GET_CLIENT_ID_REQ_MSG;
typedef struct _QMICTL_GET_CLIENT_ID_RESP_MSG {
UCHAR CtlFlags; // QMICTL_FLAG_RESPONSE
UCHAR TransactionId;
USHORT QMICTLType; // QMICTL_GET_CLIENT_ID_RESP
USHORT Length;
UCHAR TLVType; // QCTLV_TYPE_RESULT_CODE
USHORT TLVLength; // 0x0004
USHORT QMIResult; // result code
USHORT QMIError; // error code
UCHAR TLV2Type; // QCTLV_TYPE_REQUIRED_PARAMETER
USHORT TLV2Length; // 2
UCHAR QMIType;
UCHAR ClientId;
} __attribute__((packed)) QMICTL_GET_CLIENT_ID_RESP_MSG,
*PQMICTL_GET_CLIENT_ID_RESP_MSG;
typedef struct _QMICTL_RELEASE_CLIENT_ID_REQ_MSG {
UCHAR CtlFlags; // QMICTL_FLAG_REQUEST
UCHAR TransactionId;
USHORT QMICTLType; // QMICTL_RELEASE_CLIENT_ID_REQ
USHORT Length;
UCHAR TLVType; // QCTLV_TYPE_REQUIRED_PARAMETER
USHORT TLVLength; // 0x0002
UCHAR QMIType;
UCHAR ClientId;
} __attribute__((packed)) QMICTL_RELEASE_CLIENT_ID_REQ_MSG,
*PQMICTL_RELEASE_CLIENT_ID_REQ_MSG;
typedef struct _QMICTL_RELEASE_CLIENT_ID_RESP_MSG {
UCHAR CtlFlags; // QMICTL_FLAG_RESPONSE
UCHAR TransactionId;
USHORT QMICTLType; // QMICTL_RELEASE_CLIENT_ID_RESP
USHORT Length;
UCHAR TLVType; // QCTLV_TYPE_RESULT_CODE
USHORT TLVLength; // 0x0004
USHORT QMIResult; // result code
USHORT QMIError; // error code
UCHAR TLV2Type; // QCTLV_TYPE_REQUIRED_PARAMETER
USHORT TLV2Length; // 2
UCHAR QMIType;
UCHAR ClientId;
} __attribute__((packed)) QMICTL_RELEASE_CLIENT_ID_RESP_MSG,
*PQMICTL_RELEASE_CLIENT_ID_RESP_MSG;
typedef struct _QMICTL_REVOKE_CLIENT_ID_IND_MSG {
UCHAR CtlFlags; // QMICTL_FLAG_INDICATION
UCHAR TransactionId;
USHORT QMICTLType; // QMICTL_REVOKE_CLIENT_ID_IND
USHORT Length;
UCHAR TLVType; // QCTLV_TYPE_REQUIRED_PARAMETER
USHORT TLVLength; // 0x0002
UCHAR QMIType;
UCHAR ClientId;
} __attribute__((packed)) QMICTL_REVOKE_CLIENT_ID_IND_MSG,
*PQMICTL_REVOKE_CLIENT_ID_IND_MSG;
typedef struct _QMICTL_INVALID_CLIENT_ID_IND_MSG {
UCHAR CtlFlags; // QMICTL_FLAG_INDICATION
UCHAR TransactionId;
USHORT QMICTLType; // QMICTL_REVOKE_CLIENT_ID_IND
USHORT Length;
UCHAR TLVType; // QCTLV_TYPE_REQUIRED_PARAMETER
USHORT TLVLength; // 0x0002
UCHAR QMIType;
UCHAR ClientId;
} __attribute__((packed)) QMICTL_INVALID_CLIENT_ID_IND_MSG,
*PQMICTL_INVALID_CLIENT_ID_IND_MSG;
typedef struct _QMICTL_SET_DATA_FORMAT_REQ_MSG {
UCHAR CtlFlags; // QMICTL_FLAG_REQUEST
UCHAR TransactionId;
USHORT QMICTLType; // QMICTL_SET_DATA_FORMAT_REQ
USHORT Length;
UCHAR TLVType; // QCTLV_TYPE_REQUIRED_PARAMETER
USHORT TLVLength; // 1
UCHAR DataFormat; // 0-default; 1-QoS hdr present
} __attribute__((packed)) QMICTL_SET_DATA_FORMAT_REQ_MSG,
*PQMICTL_SET_DATA_FORMAT_REQ_MSG;
#ifdef QC_IP_MODE
#define SET_DATA_FORMAT_TLV_TYPE_LINK_PROTO 0x10
#define SET_DATA_FORMAT_LINK_PROTO_ETH 0x0001
#define SET_DATA_FORMAT_LINK_PROTO_IP 0x0002
typedef struct _QMICTL_SET_DATA_FORMAT_TLV_LINK_PROT {
UCHAR TLVType; // Link-Layer Protocol
USHORT TLVLength; // 2
USHORT LinkProt; // 0x1: ETH; 0x2: IP
} QMICTL_SET_DATA_FORMAT_TLV_LINK_PROT, *PQMICTL_SET_DATA_FORMAT_TLV_LINK_PROT;
#ifdef QCMP_UL_TLP
#define SET_DATA_FORMAT_TLV_TYPE_UL_TLP 0x11
typedef struct _QMICTL_SET_DATA_FORMAT_TLV_UL_TLP {
UCHAR TLVType; // 0x11, Uplink TLP Setting
USHORT TLVLength; // 1
UCHAR UlTlpSetting; // 0x0: Disable; 0x01: Enable
} QMICTL_SET_DATA_FORMAT_TLV_UL_TLP, *PQMICTL_SET_DATA_FORMAT_TLV_UL_TLP;
#endif // QCMP_UL_TLP
#ifdef QCMP_DL_TLP
#define SET_DATA_FORMAT_TLV_TYPE_DL_TLP 0x13
typedef struct _QMICTL_SET_DATA_FORMAT_TLV_DL_TLP {
UCHAR TLVType; // 0x11, Uplink TLP Setting
USHORT TLVLength; // 1
UCHAR DlTlpSetting; // 0x0: Disable; 0x01: Enable
} QMICTL_SET_DATA_FORMAT_TLV_DL_TLP, *PQMICTL_SET_DATA_FORMAT_TLV_DL_TLP;
#endif // QCMP_DL_TLP
#endif // QC_IP_MODE
#ifdef MP_QCQOS_ENABLED
#define SET_DATA_FORMAT_TLV_TYPE_QOS_SETTING 0x12
typedef struct _QMICTL_SET_DATA_FORMAT_TLV_QOS_SETTING {
UCHAR TLVType; // 0x12, QoS setting
USHORT TLVLength; // 1
UCHAR QosSetting; // 0x0: Disable; 0x01: Enable
} QMICTL_SET_DATA_FORMAT_TLV_QOS_SETTING,
*PQMICTL_SET_DATA_FORMAT_TLV_QOS_SETTING;
#endif // MP_QCQOS_ENABLED
typedef struct _QMICTL_SET_DATA_FORMAT_RESP_MSG {
UCHAR CtlFlags; // QMICTL_FLAG_RESPONSE
UCHAR TransactionId;
USHORT QMICTLType; // QMICTL_SET_DATA_FORMAT_RESP
USHORT Length;
UCHAR TLVType; // QCTLV_TYPE_RESULT_CODE
USHORT TLVLength; // 0x0004
USHORT QMIResult; // result code
USHORT QMIError; // error code
} __attribute__((packed)) QMICTL_SET_DATA_FORMAT_RESP_MSG,
*PQMICTL_SET_DATA_FORMAT_RESP_MSG;
typedef struct _QMICTL_SYNC_REQ_MSG {
UCHAR CtlFlags; // QMICTL_FLAG_REQUEST
UCHAR TransactionId;
USHORT QMICTLType; // QMICTL_CTL_SYNC_REQ
USHORT Length; // 0
} __attribute__((packed)) QMICTL_SYNC_REQ_MSG, *PQMICTL_SYNC_REQ_MSG;
typedef struct _QMICTL_SYNC_RESP_MSG {
UCHAR CtlFlags; // QMICTL_FLAG_RESPONSE
UCHAR TransactionId;
USHORT QMICTLType; // QMICTL_CTL_SYNC_RESP
USHORT Length;
UCHAR TLVType; // QCTLV_TYPE_RESULT_CODE
USHORT TLVLength; // 0x0004
USHORT QMIResult;
USHORT QMIError;
} __attribute__((packed)) QMICTL_SYNC_RESP_MSG, *PQMICTL_SYNC_RESP_MSG;
typedef struct _QMICTL_SYNC_IND_MSG {
UCHAR CtlFlags; // QMICTL_FLAG_INDICATION
UCHAR TransactionId;
USHORT QMICTLType; // QMICTL_REVOKE_CLIENT_ID_IND
USHORT Length;
} __attribute__((packed)) QMICTL_SYNC_IND_MSG, *PQMICTL_SYNC_IND_MSG;
typedef struct _QMICTL_MSG {
union {
// Message Header
QCQMICTL_MSG_HDR QMICTLMsgHdr;
QCQMICTL_MSG_HDR_RESP QMICTLMsgHdrRsp;
// QMICTL Message
QMICTL_SET_INSTANCE_ID_REQ_MSG SetInstanceIdReq;
QMICTL_SET_INSTANCE_ID_RESP_MSG SetInstanceIdRsp;
QMICTL_GET_VERSION_REQ_MSG GetVersionReq;
QMICTL_GET_VERSION_RESP_MSG GetVersionRsp;
QMICTL_GET_CLIENT_ID_REQ_MSG GetClientIdReq;
QMICTL_GET_CLIENT_ID_RESP_MSG GetClientIdRsp;
QMICTL_RELEASE_CLIENT_ID_REQ_MSG ReleaseClientIdReq;
QMICTL_RELEASE_CLIENT_ID_RESP_MSG ReleaseClientIdRsp;
QMICTL_REVOKE_CLIENT_ID_IND_MSG RevokeClientIdInd;
QMICTL_INVALID_CLIENT_ID_IND_MSG InvalidClientIdInd;
QMICTL_SET_DATA_FORMAT_REQ_MSG SetDataFormatReq;
QMICTL_SET_DATA_FORMAT_RESP_MSG SetDataFormatRsp;
QMICTL_SYNC_REQ_MSG SyncReq;
QMICTL_SYNC_RESP_MSG SyncRsp;
QMICTL_SYNC_IND_MSG SyncInd;
};
} __attribute__((packed)) QMICTL_MSG, *PQMICTL_MSG;
#endif // MPQCTL_H

287
fibocom-dial/src/MPQMI.h Normal file
View File

@ -0,0 +1,287 @@
/*===========================================================================
M P Q M I. H
DESCRIPTION:
This module contains forward references to the QMI module.
INITIALIZATION AND SEQUENCING REQUIREMENTS:
Copyright (C) 2011 by Qualcomm Technologies, Incorporated. All Rights Reserved.
===========================================================================*/
/*===========================================================================
EDIT HISTORY FOR FILE
$Header: //depot/QMI/win/qcdrivers/ndis/MPQMI.h#3 $
when who what, where, why
-------- --- ----------------------------------------------------------
11/20/04 hg Initial version.
===========================================================================*/
#ifndef USBQMI_H
#define USBQMI_H
typedef char CHAR;
typedef unsigned char UCHAR;
typedef unsigned short USHORT;
typedef int INT;
typedef unsigned int UINT;
typedef long LONG;
typedef unsigned int ULONG;
typedef unsigned long long ULONG64;
typedef char *PCHAR;
typedef unsigned char *PUCHAR;
typedef int *PINT;
typedef int BOOL;
#define TRUE (1 == 1)
#define FALSE (1 != 1)
#define QMICTL_SUPPORTED_MAJOR_VERSION 1
#define QMICTL_SUPPORTED_MINOR_VERSION 0
#pragma pack(push, 1)
// ========= USB Control Message ==========
#define USB_CTL_MSG_TYPE_QMI 0x01
// USB Control Message
typedef struct _QCUSB_CTL_MSG_HDR {
UCHAR IFType;
} __attribute__((packed)) QCUSB_CTL_MSG_HDR, *PQCUSB_CTL_MSG_HDR;
#define QCUSB_CTL_MSG_HDR_SIZE sizeof(QCUSB_CTL_MSG_HDR)
typedef struct _QCUSB_CTL_MSG {
UCHAR IFType;
UCHAR Message;
} __attribute__((packed)) QCUSB_CTL_MSG, *PQCUSB_CTL_MSG;
#define QCTLV_TYPE_REQUIRED_PARAMETER 0x01
#define QCTLV_TYPE_RESULT_CODE 0x02
// ================= QMI ==================
// Define QMI Type
typedef enum _QMI_SERVICE_TYPE {
QMUX_TYPE_CTL = 0x00,
QMUX_TYPE_WDS = 0x01,
QMUX_TYPE_DMS = 0x02,
QMUX_TYPE_NAS = 0x03,
QMUX_TYPE_QOS = 0x04,
QMUX_TYPE_WMS = 0x05,
QMUX_TYPE_PDS = 0x06,
QMUX_TYPE_UIM = 0x0B,
QMUX_TYPE_WDS_IPV6 = 0x11,
QMUX_TYPE_WDS_ADMIN = 0x1A,
QMUX_TYPE_MAX = 0xFF,
QMUX_TYPE_ALL = 0xFF
} QMI_SERVICE_TYPE;
typedef enum _QMI_RESULT_CODE_TYPE {
QMI_RESULT_SUCCESS = 0x0000,
QMI_RESULT_FAILURE = 0x0001
} QMI_RESULT_CODE_TYPE;
typedef enum _QMI_ERROR_CODE_TYPE {
QMI_ERR_NONE = 0x0000,
QMI_ERR_MALFORMED_MSG = 0x0001,
QMI_ERR_NO_MEMORY = 0x0002,
QMI_ERR_INTERNAL = 0x0003,
QMI_ERR_ABORTED = 0x0004,
QMI_ERR_CLIENT_IDS_EXHAUSTED = 0x0005,
QMI_ERR_UNABORTABLE_TRANSACTION = 0x0006,
QMI_ERR_INVALID_CLIENT_ID = 0x0007,
QMI_ERR_NO_THRESHOLDS = 0x0008,
QMI_ERR_INVALID_HANDLE = 0x0009,
QMI_ERR_INVALID_PROFILE = 0x000A,
QMI_ERR_INVALID_PINID = 0x000B,
QMI_ERR_INCORRECT_PIN = 0x000C,
QMI_ERR_NO_NETWORK_FOUND = 0x000D,
QMI_ERR_CALL_FAILED = 0x000E,
QMI_ERR_OUT_OF_CALL = 0x000F,
QMI_ERR_NOT_PROVISIONED = 0x0010,
QMI_ERR_MISSING_ARG = 0x0011,
QMI_ERR_ARG_TOO_LONG = 0x0013,
QMI_ERR_INVALID_TX_ID = 0x0016,
QMI_ERR_DEVICE_IN_USE = 0x0017,
QMI_ERR_OP_NETWORK_UNSUPPORTED = 0x0018,
QMI_ERR_OP_DEVICE_UNSUPPORTED = 0x0019,
QMI_ERR_NO_EFFECT = 0x001A,
QMI_ERR_NO_FREE_PROFILE = 0x001B,
QMI_ERR_INVALID_PDP_TYPE = 0x001C,
QMI_ERR_INVALID_TECH_PREF = 0x001D,
QMI_ERR_INVALID_PROFILE_TYPE = 0x001E,
QMI_ERR_INVALID_SERVICE_TYPE = 0x001F,
QMI_ERR_INVALID_REGISTER_ACTION = 0x0020,
QMI_ERR_INVALID_PS_ATTACH_ACTION = 0x0021,
QMI_ERR_AUTHENTICATION_FAILED = 0x0022,
QMI_ERR_PIN_BLOCKED = 0x0023,
QMI_ERR_PIN_PERM_BLOCKED = 0x0024,
QMI_ERR_SIM_NOT_INITIALIZED = 0x0025,
QMI_ERR_MAX_QOS_REQUESTS_IN_USE = 0x0026,
QMI_ERR_INCORRECT_FLOW_FILTER = 0x0027,
QMI_ERR_NETWORK_QOS_UNAWARE = 0x0028,
QMI_ERR_INVALID_QOS_ID = 0x0029,
QMI_ERR_INVALID_ID = 0x0029,
QMI_ERR_REQUESTED_NUM_UNSUPPORTED = 0x002A,
QMI_ERR_INTERFACE_NOT_FOUND = 0x002B,
QMI_ERR_FLOW_SUSPENDED = 0x002C,
QMI_ERR_INVALID_DATA_FORMAT = 0x002D,
QMI_ERR_GENERAL = 0x002E,
QMI_ERR_UNKNOWN = 0x002F,
QMI_ERR_INVALID_ARG = 0x0030,
QMI_ERR_INVALID_INDEX = 0x0031,
QMI_ERR_NO_ENTRY = 0x0032,
QMI_ERR_DEVICE_STORAGE_FULL = 0x0033,
QMI_ERR_DEVICE_NOT_READY = 0x0034,
QMI_ERR_NETWORK_NOT_READY = 0x0035,
QMI_ERR_CAUSE_CODE = 0x0036,
QMI_ERR_MESSAGE_NOT_SENT = 0x0037,
QMI_ERR_MESSAGE_DELIVERY_FAILURE = 0x0038,
QMI_ERR_INVALID_MESSAGE_ID = 0x0039,
QMI_ERR_ENCODING = 0x003A,
QMI_ERR_AUTHENTICATION_LOCK = 0x003B,
QMI_ERR_INVALID_TRANSITION = 0x003C,
QMI_ERR_NOT_A_MCAST_IFACE = 0x003D,
QMI_ERR_MAX_MCAST_REQUESTS_IN_USE = 0x003E,
QMI_ERR_INVALID_MCAST_HANDLE = 0x003F,
QMI_ERR_INVALID_IP_FAMILY_PREF = 0x0040,
QMI_ERR_SESSION_INACTIVE = 0x0041,
QMI_ERR_SESSION_INVALID = 0x0042,
QMI_ERR_SESSION_OWNERSHIP = 0x0043,
QMI_ERR_INSUFFICIENT_RESOURCES = 0x0044,
QMI_ERR_DISABLED = 0x0045,
QMI_ERR_INVALID_OPERATION = 0x0046,
QMI_ERR_INVALID_QMI_CMD = 0x0047,
QMI_ERR_TPDU_TYPE = 0x0048,
QMI_ERR_SMSC_ADDR = 0x0049,
QMI_ERR_INFO_UNAVAILABLE = 0x004A,
QMI_ERR_SEGMENT_TOO_LONG = 0x004B,
QMI_ERR_SEGMENT_ORDER = 0x004C,
QMI_ERR_BUNDLING_NOT_SUPPORTED = 0x004D,
QMI_ERR_OP_PARTIAL_FAILURE = 0x004E,
QMI_ERR_POLICY_MISMATCH = 0x004F,
QMI_ERR_SIM_FILE_NOT_FOUND = 0x0050,
QMI_ERR_EXTENDED_INTERNAL = 0x0051,
QMI_ERR_ACCESS_DENIED = 0x0052,
QMI_ERR_HARDWARE_RESTRICTED = 0x0053,
QMI_ERR_ACK_NOT_SENT = 0x0054,
QMI_ERR_INJECT_TIMEOUT = 0x0055,
QMI_ERR_INCOMPATIBLE_STATE = 0x005A,
QMI_ERR_FDN_RESTRICT = 0x005B,
QMI_ERR_SUPS_FAILURE_CAUSE = 0x005C,
QMI_ERR_NO_RADIO = 0x005D,
QMI_ERR_NOT_SUPPORTED = 0x005E,
QMI_ERR_NO_SUBSCRIPTION = 0x005F,
QMI_ERR_CARD_CALL_CONTROL_FAILED = 0x0060,
QMI_ERR_NETWORK_ABORTED = 0x0061,
QMI_ERR_MSG_BLOCKED = 0x0062,
QMI_ERR_INVALID_SESSION_TYPE = 0x0064,
QMI_ERR_INVALID_PB_TYPE = 0x0065,
QMI_ERR_NO_SIM = 0x0066,
QMI_ERR_PB_NOT_READY = 0x0067,
QMI_ERR_PIN_RESTRICTION = 0x0068,
QMI_ERR_PIN2_RESTRICTION = 0x0069,
QMI_ERR_PUK_RESTRICTION = 0x006A,
QMI_ERR_PUK2_RESTRICTION = 0x006B,
QMI_ERR_PB_ACCESS_RESTRICTED = 0x006C,
QMI_ERR_PB_DELETE_IN_PROG = 0x006D,
QMI_ERR_PB_TEXT_TOO_LONG = 0x006E,
QMI_ERR_PB_NUMBER_TOO_LONG = 0x006F,
QMI_ERR_PB_HIDDEN_KEY_RESTRICTION = 0x0070
} QMI_ERROR_CODE_TYPE;
#define QCQMI_CTL_FLAG_SERVICE 0x80
#define QCQMI_CTL_FLAG_CTL_POINT 0x00
typedef struct _QCQMI_HDR {
UCHAR IFType;
USHORT Length;
UCHAR CtlFlags; // reserved
UCHAR QMIType;
UCHAR ClientId;
} __attribute__((packed)) QCQMI_HDR, *PQCQMI_HDR;
#define QCQMI_HDR_SIZE (sizeof(QCQMI_HDR) - 1)
typedef struct _QCQMI {
UCHAR IFType;
USHORT Length;
UCHAR CtlFlags; // reserved
UCHAR QMIType;
UCHAR ClientId;
UCHAR SDU;
} __attribute__((packed)) QCQMI, *PQCQMI;
typedef struct _QMI_SERVICE_VERSION {
USHORT Major;
USHORT Minor;
USHORT AddendumMajor;
USHORT AddendumMinor;
} __attribute__((packed)) QMI_SERVICE_VERSION, *PQMI_SERVICE_VERSION;
// ================= QMUX ==================
#define QMUX_MSG_OVERHEAD_BYTES 4 // Type(USHORT) Length(USHORT) -- header
#define QMUX_BROADCAST_CID 0xFF
typedef struct _QCQMUX_HDR {
UCHAR CtlFlags; // 0: single QMUX Msg; 1:
USHORT TransactionId;
} __attribute__((packed)) QCQMUX_HDR, *PQCQMUX_HDR;
typedef struct _QCQMUX {
UCHAR CtlFlags; // 0: single QMUX Msg; 1:
USHORT TransactionId;
UCHAR Message; // Type(2), Length(2), Value
} __attribute__((packed)) QCQMUX, *PQCQMUX;
#define QCQMUX_HDR_SIZE sizeof(QCQMUX_HDR)
typedef struct _QCQMUX_MSG_HDR {
USHORT Type;
USHORT Length;
} __attribute__((packed)) QCQMUX_MSG_HDR, *PQCQMUX_MSG_HDR;
#define QCQMUX_MSG_HDR_SIZE sizeof(QCQMUX_MSG_HDR)
typedef struct _QCQMUX_MSG_HDR_RESP {
USHORT Type;
USHORT Length;
UCHAR TLVType; // 0x02 - result code
USHORT TLVLength; // 4
USHORT QMUXResult; // QMI_RESULT_SUCCESS
// QMI_RESULT_FAILURE
USHORT QMUXError; // QMI_ERR_INVALID_ARG
// QMI_ERR_NO_MEMORY
// QMI_ERR_INTERNAL
// QMI_ERR_FAULT
} __attribute__((packed)) QCQMUX_MSG_HDR_RESP, *PQCQMUX_MSG_HDR_RESP;
typedef struct _QCQMUX_TLV {
UCHAR Type;
USHORT Length;
UCHAR Value;
} __attribute__((packed)) QCQMUX_TLV, *PQCQMUX_TLV;
typedef struct _QMI_TLV_HDR {
UCHAR TLVType;
USHORT TLVLength;
} __attribute__((packed)) QMI_TLV_HDR, *PQMI_TLV_HDR;
// QMUX Message Definitions -- QMI SDU
#define QMUX_CTL_FLAG_SINGLE_MSG 0x00
#define QMUX_CTL_FLAG_COMPOUND_MSG 0x01
#define QMUX_CTL_FLAG_TYPE_CMD 0x00
#define QMUX_CTL_FLAG_TYPE_RSP 0x02
#define QMUX_CTL_FLAG_TYPE_IND 0x04
#define QMUX_CTL_FLAG_MASK_COMPOUND 0x01
#define QMUX_CTL_FLAG_MASK_TYPE 0x06 // 00-cmd, 01-rsp, 10-ind
#pragma pack(pop)
#endif // USBQMI_H

437
fibocom-dial/src/MPQMUX.c Normal file
View File

@ -0,0 +1,437 @@
#include "QMIThread.h"
static char line[1024];
static pthread_mutex_t dumpQMIMutex = PTHREAD_MUTEX_INITIALIZER;
#undef dbg
#define dbg(format, arg...) \
do { \
if (strlen(line) < sizeof(line)) \
snprintf(&line[strlen(line)], sizeof(line) - strlen(line), format, \
##arg); \
} while (0)
PQMI_TLV_HDR GetTLV(PQCQMUX_MSG_HDR pQMUXMsgHdr, int TLVType);
typedef struct {
UINT type;
const char *name;
} QMI_NAME_T;
#define qmi_name_item(type) \
{ \
type, #type \
}
static const QMI_NAME_T qmux_ctl_QMICTLType[] = {
// QMICTL Type
qmi_name_item(QMICTL_SET_INSTANCE_ID_REQ), // 0x0020
qmi_name_item(QMICTL_SET_INSTANCE_ID_RESP), // 0x0020
qmi_name_item(QMICTL_GET_VERSION_REQ), // 0x0021
qmi_name_item(QMICTL_GET_VERSION_RESP), // 0x0021
qmi_name_item(QMICTL_GET_CLIENT_ID_REQ), // 0x0022
qmi_name_item(QMICTL_GET_CLIENT_ID_RESP), // 0x0022
qmi_name_item(QMICTL_RELEASE_CLIENT_ID_REQ), // 0x0023
qmi_name_item(QMICTL_RELEASE_CLIENT_ID_RESP), // 0x0023
qmi_name_item(QMICTL_REVOKE_CLIENT_ID_IND), // 0x0024
qmi_name_item(QMICTL_INVALID_CLIENT_ID_IND), // 0x0025
qmi_name_item(QMICTL_SET_DATA_FORMAT_REQ), // 0x0026
qmi_name_item(QMICTL_SET_DATA_FORMAT_RESP), // 0x0026
qmi_name_item(QMICTL_SYNC_REQ), // 0x0027
qmi_name_item(QMICTL_SYNC_RESP), // 0x0027
qmi_name_item(QMICTL_SYNC_IND), // 0x0027
};
static const QMI_NAME_T qmux_CtlFlags[] = {
qmi_name_item(QMUX_CTL_FLAG_TYPE_CMD),
qmi_name_item(QMUX_CTL_FLAG_TYPE_RSP),
qmi_name_item(QMUX_CTL_FLAG_TYPE_IND),
};
static const QMI_NAME_T qmux_wds_Type[] = {
qmi_name_item(QMIWDS_SET_EVENT_REPORT_REQ), // 0x0001
qmi_name_item(QMIWDS_SET_EVENT_REPORT_RESP), // 0x0001
qmi_name_item(QMIWDS_EVENT_REPORT_IND), // 0x0001
qmi_name_item(QMIWDS_START_NETWORK_INTERFACE_REQ), // 0x0020
qmi_name_item(QMIWDS_START_NETWORK_INTERFACE_RESP), // 0x0020
qmi_name_item(QMIWDS_STOP_NETWORK_INTERFACE_REQ), // 0x0021
qmi_name_item(QMIWDS_STOP_NETWORK_INTERFACE_RESP), // 0x0021
qmi_name_item(QMIWDS_GET_PKT_SRVC_STATUS_REQ), // 0x0022
qmi_name_item(QMIWDS_GET_PKT_SRVC_STATUS_RESP), // 0x0022
qmi_name_item(QMIWDS_GET_PKT_SRVC_STATUS_IND), // 0x0022
qmi_name_item(QMIWDS_GET_CURRENT_CHANNEL_RATE_REQ), // 0x0023
qmi_name_item(QMIWDS_GET_CURRENT_CHANNEL_RATE_RESP), // 0x0023
qmi_name_item(QMIWDS_GET_PKT_STATISTICS_REQ), // 0x0024
qmi_name_item(QMIWDS_GET_PKT_STATISTICS_RESP), // 0x0024
//begin modified by zhangkaibo add create profile qmi. mantis 0049137,0048741 20200610
qmi_name_item(QMIWDS_CREATE_PROFILE_SETTINGS_REQ), // 0x0027
qmi_name_item(QMIWDS_CREATE_PROFILE_SETTINGS_RESP), // 0x0027
//end modified by zhangkaibo add create profile qmi. mantis 0049137,0048741 20200610
qmi_name_item(QMIWDS_MODIFY_PROFILE_SETTINGS_REQ), // 0x0028
qmi_name_item(QMIWDS_MODIFY_PROFILE_SETTINGS_RESP), // 0x0028
qmi_name_item(QMIWDS_GET_PROFILE_SETTINGS_REQ), // 0x002B
qmi_name_item(QMIWDS_GET_PROFILE_SETTINGS_RESP), // 0x002BD
qmi_name_item(QMIWDS_GET_DEFAULT_SETTINGS_REQ), // 0x002C
qmi_name_item(QMIWDS_GET_DEFAULT_SETTINGS_RESP), // 0x002C
qmi_name_item(QMIWDS_GET_RUNTIME_SETTINGS_REQ), // 0x002D
qmi_name_item(QMIWDS_GET_RUNTIME_SETTINGS_RESP), // 0x002D
qmi_name_item(QMIWDS_GET_MIP_MODE_REQ), // 0x002F
qmi_name_item(QMIWDS_GET_MIP_MODE_RESP), // 0x002F
qmi_name_item(QMIWDS_GET_DATA_BEARER_REQ), // 0x0037
qmi_name_item(QMIWDS_GET_DATA_BEARER_RESP), // 0x0037
qmi_name_item(QMIWDS_DUN_CALL_INFO_REQ), // 0x0038
qmi_name_item(QMIWDS_DUN_CALL_INFO_RESP), // 0x0038
qmi_name_item(QMIWDS_DUN_CALL_INFO_IND), // 0x0038
qmi_name_item(QMIWDS_SET_CLIENT_IP_FAMILY_PREF_REQ), // 0x004D
qmi_name_item(QMIWDS_SET_CLIENT_IP_FAMILY_PREF_RESP), // 0x004D
qmi_name_item(QMIWDS_SET_AUTO_CONNECT_REQ), // 0x0051
qmi_name_item(QMIWDS_SET_AUTO_CONNECT_RESP), // 0x0051
qmi_name_item(QMIWDS_BIND_MUX_DATA_PORT_REQ), // 0x00A2
qmi_name_item(QMIWDS_BIND_MUX_DATA_PORT_RESP), // 0x00A2
};
static const QMI_NAME_T qmux_dms_Type[] = {
// ======================= DMS ==============================
qmi_name_item(QMIDMS_SET_EVENT_REPORT_REQ), // 0x0001
qmi_name_item(QMIDMS_SET_EVENT_REPORT_RESP), // 0x0001
qmi_name_item(QMIDMS_EVENT_REPORT_IND), // 0x0001
qmi_name_item(QMIDMS_GET_DEVICE_CAP_REQ), // 0x0020
qmi_name_item(QMIDMS_GET_DEVICE_CAP_RESP), // 0x0020
qmi_name_item(QMIDMS_GET_DEVICE_MFR_REQ), // 0x0021
qmi_name_item(QMIDMS_GET_DEVICE_MFR_RESP), // 0x0021
qmi_name_item(QMIDMS_GET_DEVICE_MODEL_ID_REQ), // 0x0022
qmi_name_item(QMIDMS_GET_DEVICE_MODEL_ID_RESP), // 0x0022
qmi_name_item(QMIDMS_GET_DEVICE_REV_ID_REQ), // 0x0023
qmi_name_item(QMIDMS_GET_DEVICE_REV_ID_RESP), // 0x0023
qmi_name_item(QMIDMS_GET_MSISDN_REQ), // 0x0024
qmi_name_item(QMIDMS_GET_MSISDN_RESP), // 0x0024
qmi_name_item(QMIDMS_GET_DEVICE_SERIAL_NUMBERS_REQ), // 0x0025
qmi_name_item(QMIDMS_GET_DEVICE_SERIAL_NUMBERS_RESP), // 0x0025
qmi_name_item(QMIDMS_UIM_SET_PIN_PROTECTION_REQ), // 0x0027
qmi_name_item(QMIDMS_UIM_SET_PIN_PROTECTION_RESP), // 0x0027
qmi_name_item(QMIDMS_UIM_VERIFY_PIN_REQ), // 0x0028
qmi_name_item(QMIDMS_UIM_VERIFY_PIN_RESP), // 0x0028
qmi_name_item(QMIDMS_UIM_UNBLOCK_PIN_REQ), // 0x0029
qmi_name_item(QMIDMS_UIM_UNBLOCK_PIN_RESP), // 0x0029
qmi_name_item(QMIDMS_UIM_CHANGE_PIN_REQ), // 0x002A
qmi_name_item(QMIDMS_UIM_CHANGE_PIN_RESP), // 0x002A
qmi_name_item(QMIDMS_UIM_GET_PIN_STATUS_REQ), // 0x002B
qmi_name_item(QMIDMS_UIM_GET_PIN_STATUS_RESP), // 0x002B
qmi_name_item(QMIDMS_GET_DEVICE_HARDWARE_REV_REQ), // 0x002C
qmi_name_item(QMIDMS_GET_DEVICE_HARDWARE_REV_RESP), // 0x002C
qmi_name_item(QMIDMS_GET_OPERATING_MODE_REQ), // 0x002D
qmi_name_item(QMIDMS_GET_OPERATING_MODE_RESP), // 0x002D
qmi_name_item(QMIDMS_SET_OPERATING_MODE_REQ), // 0x002E
qmi_name_item(QMIDMS_SET_OPERATING_MODE_RESP), // 0x002E
qmi_name_item(QMIDMS_GET_ACTIVATED_STATUS_REQ), // 0x0031
qmi_name_item(QMIDMS_GET_ACTIVATED_STATUS_RESP), // 0x0031
qmi_name_item(QMIDMS_ACTIVATE_AUTOMATIC_REQ), // 0x0032
qmi_name_item(QMIDMS_ACTIVATE_AUTOMATIC_RESP), // 0x0032
qmi_name_item(QMIDMS_ACTIVATE_MANUAL_REQ), // 0x0033
qmi_name_item(QMIDMS_ACTIVATE_MANUAL_RESP), // 0x0033
qmi_name_item(QMIDMS_UIM_GET_ICCID_REQ), // 0x003C
qmi_name_item(QMIDMS_UIM_GET_ICCID_RESP), // 0x003C
qmi_name_item(QMIDMS_UIM_GET_CK_STATUS_REQ), // 0x0040
qmi_name_item(QMIDMS_UIM_GET_CK_STATUS_RESP), // 0x0040
qmi_name_item(QMIDMS_UIM_SET_CK_PROTECTION_REQ), // 0x0041
qmi_name_item(QMIDMS_UIM_SET_CK_PROTECTION_RESP), // 0x0041
qmi_name_item(QMIDMS_UIM_UNBLOCK_CK_REQ), // 0x0042
qmi_name_item(QMIDMS_UIM_UNBLOCK_CK_RESP), // 0x0042
qmi_name_item(QMIDMS_UIM_GET_IMSI_REQ), // 0x0043
qmi_name_item(QMIDMS_UIM_GET_IMSI_RESP), // 0x0043
qmi_name_item(QMIDMS_UIM_GET_STATE_REQ), // 0x0044
qmi_name_item(QMIDMS_UIM_GET_STATE_RESP), // 0x0044
qmi_name_item(QMIDMS_GET_BAND_CAP_REQ), // 0x0045
qmi_name_item(QMIDMS_GET_BAND_CAP_RESP), // 0x0045
};
static const QMI_NAME_T qmux_nas_Type[] = {
// ======================= NAS ==============================
qmi_name_item(QMINAS_SET_EVENT_REPORT_REQ), // 0x0002
qmi_name_item(QMINAS_SET_EVENT_REPORT_RESP), // 0x0002
qmi_name_item(QMINAS_EVENT_REPORT_IND), // 0x0002
qmi_name_item(QMINAS_GET_SIGNAL_STRENGTH_REQ), // 0x0020
qmi_name_item(QMINAS_GET_SIGNAL_STRENGTH_RESP), // 0x0020
qmi_name_item(QMINAS_PERFORM_NETWORK_SCAN_REQ), // 0x0021
qmi_name_item(QMINAS_PERFORM_NETWORK_SCAN_RESP), // 0x0021
qmi_name_item(QMINAS_INITIATE_NW_REGISTER_REQ), // 0x0022
qmi_name_item(QMINAS_INITIATE_NW_REGISTER_RESP), // 0x0022
qmi_name_item(QMINAS_INITIATE_ATTACH_REQ), // 0x0023
qmi_name_item(QMINAS_INITIATE_ATTACH_RESP), // 0x0023
qmi_name_item(QMINAS_GET_SERVING_SYSTEM_REQ), // 0x0024
qmi_name_item(QMINAS_GET_SERVING_SYSTEM_RESP), // 0x0024
qmi_name_item(QMINAS_SERVING_SYSTEM_IND), // 0x0024
qmi_name_item(QMINAS_GET_HOME_NETWORK_REQ), // 0x0025
qmi_name_item(QMINAS_GET_HOME_NETWORK_RESP), // 0x0025
qmi_name_item(QMINAS_GET_PREFERRED_NETWORK_REQ), // 0x0026
qmi_name_item(QMINAS_GET_PREFERRED_NETWORK_RESP), // 0x0026
qmi_name_item(QMINAS_SET_PREFERRED_NETWORK_REQ), // 0x0027
qmi_name_item(QMINAS_SET_PREFERRED_NETWORK_RESP), // 0x0027
qmi_name_item(QMINAS_GET_FORBIDDEN_NETWORK_REQ), // 0x0028
qmi_name_item(QMINAS_GET_FORBIDDEN_NETWORK_RESP), // 0x0028
qmi_name_item(QMINAS_SET_FORBIDDEN_NETWORK_REQ), // 0x0029
qmi_name_item(QMINAS_SET_FORBIDDEN_NETWORK_RESP), // 0x0029
qmi_name_item(QMINAS_SET_TECHNOLOGY_PREF_REQ), // 0x002A
qmi_name_item(QMINAS_SET_TECHNOLOGY_PREF_RESP), // 0x002A
qmi_name_item(QMINAS_GET_RF_BAND_INFO_REQ), // 0x0031
qmi_name_item(QMINAS_GET_RF_BAND_INFO_RESP), // 0x0031
qmi_name_item(QMINAS_GET_PLMN_NAME_REQ), // 0x0044
qmi_name_item(QMINAS_GET_PLMN_NAME_RESP), // 0x0044
qmi_name_item(FIBO_PACKET_TRANSFER_START_IND), // 0X100
qmi_name_item(FIBO_PACKET_TRANSFER_END_IND), // 0X101
qmi_name_item(QMINAS_GET_SYS_INFO_REQ), // 0x004D
qmi_name_item(QMINAS_GET_SYS_INFO_RESP), // 0x004D
qmi_name_item(QMINAS_SYS_INFO_IND), // 0x004D
};
static const QMI_NAME_T qmux_wms_Type[] = {
// ======================= WMS ==============================
qmi_name_item(QMIWMS_SET_EVENT_REPORT_REQ), // 0x0001
qmi_name_item(QMIWMS_SET_EVENT_REPORT_RESP), // 0x0001
qmi_name_item(QMIWMS_EVENT_REPORT_IND), // 0x0001
qmi_name_item(QMIWMS_RAW_SEND_REQ), // 0x0020
qmi_name_item(QMIWMS_RAW_SEND_RESP), // 0x0020
qmi_name_item(QMIWMS_RAW_WRITE_REQ), // 0x0021
qmi_name_item(QMIWMS_RAW_WRITE_RESP), // 0x0021
qmi_name_item(QMIWMS_RAW_READ_REQ), // 0x0022
qmi_name_item(QMIWMS_RAW_READ_RESP), // 0x0022
qmi_name_item(QMIWMS_MODIFY_TAG_REQ), // 0x0023
qmi_name_item(QMIWMS_MODIFY_TAG_RESP), // 0x0023
qmi_name_item(QMIWMS_DELETE_REQ), // 0x0024
qmi_name_item(QMIWMS_DELETE_RESP), // 0x0024
qmi_name_item(QMIWMS_GET_MESSAGE_PROTOCOL_REQ), // 0x0030
qmi_name_item(QMIWMS_GET_MESSAGE_PROTOCOL_RESP), // 0x0030
qmi_name_item(QMIWMS_LIST_MESSAGES_REQ), // 0x0031
qmi_name_item(QMIWMS_LIST_MESSAGES_RESP), // 0x0031
qmi_name_item(QMIWMS_GET_SMSC_ADDRESS_REQ), // 0x0034
qmi_name_item(QMIWMS_GET_SMSC_ADDRESS_RESP), // 0x0034
qmi_name_item(QMIWMS_SET_SMSC_ADDRESS_REQ), // 0x0035
qmi_name_item(QMIWMS_SET_SMSC_ADDRESS_RESP), // 0x0035
qmi_name_item(QMIWMS_GET_STORE_MAX_SIZE_REQ), // 0x0036
qmi_name_item(QMIWMS_GET_STORE_MAX_SIZE_RESP), // 0x0036
};
static const QMI_NAME_T qmux_wds_admin_Type[] = {
qmi_name_item(QMIWDS_ADMIN_SET_DATA_FORMAT_REQ), // 0x0020
qmi_name_item(QMIWDS_ADMIN_SET_DATA_FORMAT_RESP), // 0x0020
qmi_name_item(QMIWDS_ADMIN_GET_DATA_FORMAT_REQ), // 0x0021
qmi_name_item(QMIWDS_ADMIN_GET_DATA_FORMAT_RESP), // 0x0021
qmi_name_item(QMIWDS_ADMIN_SET_QMAP_SETTINGS_REQ), // 0x002B
qmi_name_item(QMIWDS_ADMIN_SET_QMAP_SETTINGS_RESP), // 0x002B
qmi_name_item(QMIWDS_ADMIN_GET_QMAP_SETTINGS_REQ), // 0x002C
qmi_name_item(QMIWDS_ADMIN_GET_QMAP_SETTINGS_RESP), // 0x002C
};
static const QMI_NAME_T qmux_uim_Type[] = {
qmi_name_item(QMIUIM_READ_TRANSPARENT_REQ), // 0x0020
qmi_name_item(QMIUIM_READ_TRANSPARENT_RESP), // 0x0020
qmi_name_item(QMIUIM_READ_TRANSPARENT_IND), // 0x0020
qmi_name_item(QMIUIM_READ_RECORD_REQ), // 0x0021
qmi_name_item(QMIUIM_READ_RECORD_RESP), // 0x0021
qmi_name_item(QMIUIM_READ_RECORD_IND), // 0x0021
qmi_name_item(QMIUIM_WRITE_TRANSPARENT_REQ), // 0x0022
qmi_name_item(QMIUIM_WRITE_TRANSPARENT_RESP), // 0x0022
qmi_name_item(QMIUIM_WRITE_TRANSPARENT_IND), // 0x0022
qmi_name_item(QMIUIM_WRITE_RECORD_REQ), // 0x0023
qmi_name_item(QMIUIM_WRITE_RECORD_RESP), // 0x0023
qmi_name_item(QMIUIM_WRITE_RECORD_IND), // 0x0023
qmi_name_item(QMIUIM_SET_PIN_PROTECTION_REQ), // 0x0025
qmi_name_item(QMIUIM_SET_PIN_PROTECTION_RESP), // 0x0025
qmi_name_item(QMIUIM_SET_PIN_PROTECTION_IND), // 0x0025
qmi_name_item(QMIUIM_VERIFY_PIN_REQ), // 0x0026
qmi_name_item(QMIUIM_VERIFY_PIN_RESP), // 0x0026
qmi_name_item(QMIUIM_VERIFY_PIN_IND), // 0x0026
qmi_name_item(QMIUIM_UNBLOCK_PIN_REQ), // 0x0027
qmi_name_item(QMIUIM_UNBLOCK_PIN_RESP), // 0x0027
qmi_name_item(QMIUIM_UNBLOCK_PIN_IND), // 0x0027
qmi_name_item(QMIUIM_CHANGE_PIN_REQ), // 0x0028
qmi_name_item(QMIUIM_CHANGE_PIN_RESP), // 0x0028
qmi_name_item(QMIUIM_CHANGE_PIN_IND), // 0x0028
qmi_name_item(QMIUIM_DEPERSONALIZATION_REQ), // 0x0029
qmi_name_item(QMIUIM_DEPERSONALIZATION_RESP), // 0x0029
qmi_name_item(QMIUIM_EVENT_REG_REQ), // 0x002E
qmi_name_item(QMIUIM_EVENT_REG_RESP), // 0x002E
qmi_name_item(QMIUIM_GET_CARD_STATUS_REQ), // 0x002F
qmi_name_item(QMIUIM_GET_CARD_STATUS_RESP), // 0x002F
qmi_name_item(QMIUIM_STATUS_CHANGE_IND), // 0x0032
};
static const char *qmi_name_get(const QMI_NAME_T *table, size_t size, int type,
const char *tag)
{
static char unknow[40];
size_t i;
if (qmux_CtlFlags == table) {
if (!strcmp(tag, "_REQ"))
tag = "_CMD";
else if (!strcmp(tag, "_RESP"))
tag = "_RSP";
}
for (i = 0; i < size; i++) {
if (table[i].type == (UINT)type) {
if (!tag || (strstr(table[i].name, tag)))
return table[i].name;
}
}
sprintf(unknow, "unknow_%x", type);
return unknow;
}
#define QMI_NAME(table, type) \
qmi_name_get(table, sizeof(table) / sizeof(table[0]), type, 0)
#define QMUX_NAME(table, type, tag) \
qmi_name_get(table, sizeof(table) / sizeof(table[0]), type, tag)
void dump_tlv(PQCQMUX_MSG_HDR pQMUXMsgHdr)
{
int TLVFind = 0;
int i;
// dbg("QCQMUX_TLV-----------------------------------\n");
// dbg("{Type,\tLength,\tValue}\n");
while (1) {
PQMI_TLV_HDR TLVHdr = GetTLV(pQMUXMsgHdr, 0x1000 + (++TLVFind));
if (TLVHdr == NULL)
break;
// if ((TLVHdr->TLVType == 0x02) && ((USHORT *)(TLVHdr+1))[0])
{
dbg("{%02x,\t%04x,\t", TLVHdr->TLVType,
le16_to_cpu(TLVHdr->TLVLength));
for (i = 0; i < le16_to_cpu(TLVHdr->TLVLength); i++) {
dbg("%02x ", ((UCHAR *)(TLVHdr + 1))[i]);
}
dbg("}\n");
}
} // while
}
void dump_ctl(PQCQMICTL_MSG_HDR CTLHdr)
{
const char *tag;
// dbg("QCQMICTL_MSG--------------------------------------------\n");
// dbg("CtlFlags: %02x\t\t%s\n", CTLHdr->CtlFlags,
// QMI_NAME(qmi_ctl_CtlFlags, CTLHdr->CtlFlags));
dbg("TransactionId: %02x\n", CTLHdr->TransactionId);
switch (CTLHdr->CtlFlags) {
case QMICTL_FLAG_REQUEST:
tag = "_REQ";
break;
case QMICTL_FLAG_RESPONSE:
tag = "_RESP";
break;
case QMICTL_FLAG_INDICATION:
tag = "_IND";
break;
default:
tag = 0;
break;
}
dbg("QMICTLType: %04x\t%s\n", le16_to_cpu(CTLHdr->QMICTLType),
QMUX_NAME(qmux_ctl_QMICTLType, le16_to_cpu(CTLHdr->QMICTLType), tag));
dbg("Length: %04x\n", le16_to_cpu(CTLHdr->Length));
dump_tlv((PQCQMUX_MSG_HDR)(&CTLHdr->QMICTLType));
}
int dump_qmux(QMI_SERVICE_TYPE serviceType, PQCQMUX_HDR QMUXHdr)
{
PQCQMUX_MSG_HDR QMUXMsgHdr = (PQCQMUX_MSG_HDR)(QMUXHdr + 1);
CHAR *tag;
// dbg("QCQMUX--------------------------------------------\n");
switch (QMUXHdr->CtlFlags & QMUX_CTL_FLAG_MASK_TYPE) {
case QMUX_CTL_FLAG_TYPE_CMD:
tag = "_REQ";
break;
case QMUX_CTL_FLAG_TYPE_RSP:
tag = "_RESP";
break;
case QMUX_CTL_FLAG_TYPE_IND:
tag = "_IND";
break;
default:
tag = 0;
break;
}
// dbg("CtlFlags: %02x\t\t%s\n", QMUXHdr->CtlFlags,
// QMUX_NAME(qmux_CtlFlags, QMUXHdr->CtlFlags, tag));
dbg("TransactionId: %04x\n", le16_to_cpu(QMUXHdr->TransactionId));
// dbg("QCQMUX_MSG_HDR-----------------------------------\n");
switch (serviceType) {
case QMUX_TYPE_DMS:
dbg("Type: %04x\t%s\n", le16_to_cpu(QMUXMsgHdr->Type),
QMUX_NAME(qmux_dms_Type, le16_to_cpu(QMUXMsgHdr->Type), tag));
break;
case QMUX_TYPE_NAS:
dbg("Type: %04x\t%s\n", le16_to_cpu(QMUXMsgHdr->Type),
QMUX_NAME(qmux_nas_Type, le16_to_cpu(QMUXMsgHdr->Type), tag));
break;
case QMUX_TYPE_WDS:
dbg("Type: %04x\t%s\n", le16_to_cpu(QMUXMsgHdr->Type),
QMUX_NAME(qmux_wds_Type, le16_to_cpu(QMUXMsgHdr->Type), tag));
break;
case QMUX_TYPE_WMS:
dbg("Type: %04x\t%s\n", le16_to_cpu(QMUXMsgHdr->Type),
QMUX_NAME(qmux_wms_Type, le16_to_cpu(QMUXMsgHdr->Type), tag));
break;
case QMUX_TYPE_WDS_ADMIN:
dbg("Type: %04x\t%s\n", le16_to_cpu(QMUXMsgHdr->Type),
QMUX_NAME(qmux_wds_admin_Type, le16_to_cpu(QMUXMsgHdr->Type), tag));
break;
case QMUX_TYPE_UIM:
dbg("Type: %04x\t%s\n", le16_to_cpu(QMUXMsgHdr->Type),
QMUX_NAME(qmux_uim_Type, le16_to_cpu(QMUXMsgHdr->Type), tag));
break;
case QMUX_TYPE_PDS:
case QMUX_TYPE_QOS:
case QMUX_TYPE_CTL:
default:
dbg("Type: %04x\t%s\n", le16_to_cpu(QMUXMsgHdr->Type),
"PDS/QOS/CTL/unknown!");
break;
}
dbg("Length: %04x\n", le16_to_cpu(QMUXMsgHdr->Length));
dump_tlv(QMUXMsgHdr);
return 0;
}
void dump_qmi(void *dataBuffer, int dataLen)
{
PQCQMI_HDR QMIHdr = (PQCQMI_HDR)dataBuffer;
PQCQMUX_HDR QMUXHdr = (PQCQMUX_HDR)(QMIHdr + 1);
PQCQMICTL_MSG_HDR CTLHdr = (PQCQMICTL_MSG_HDR)(QMIHdr + 1);
int i;
if (!debug_qmi)
return;
pthread_mutex_lock(&dumpQMIMutex);
line[0] = 0;
for (i = 0; i < dataLen; i++) {
dbg("%02x ", ((unsigned char *)dataBuffer)[i]);
}
dbg_time("%s", line);
line[0] = 0;
// dbg("QCQMI_HDR-----------------------------------------");
// dbg("IFType: %02x\t\t%s", QMIHdr->IFType,
// QMI_NAME(qmi_IFType, QMIHdr->IFType)); dbg("Length: %04x",
// le16_to_cpu(QMIHdr->Length)); dbg("CtlFlags: %02x\t\t%s",
// QMIHdr->CtlFlags, QMI_NAME(qmi_CtlFlags, QMIHdr->CtlFlags));
// dbg("QMIType: %02x\t\t%s", QMIHdr->QMIType, QMI_NAME(qmi_QMIType,
// QMIHdr->QMIType)); dbg("ClientId: %02x", QMIHdr->ClientId);
if (QMIHdr->QMIType == QMUX_TYPE_CTL) {
dump_ctl(CTLHdr);
} else {
dump_qmux(QMIHdr->QMIType, QMUXHdr);
}
dbg_time("%s", line);
pthread_mutex_unlock(&dumpQMIMutex);
}

3671
fibocom-dial/src/MPQMUX.h Normal file

File diff suppressed because it is too large Load Diff

42
fibocom-dial/src/Makefile Normal file
View File

@ -0,0 +1,42 @@
ifneq ($(CROSS_COMPILE),)
CROSS-COMPILE:=$(CROSS_COMPILE)
endif
CFLAGS += -DGHT_FEATURE_PCIE_AUTO
ifeq ($(CC),cc)
CC:=$(CROSS-COMPILE)gcc
endif
LD:=$(CROSS-COMPILE)ld
SRC=QmiWwanCM.c GobiNetCM.c main.c MPQMUX.c QMIThread.c util.c qmap_bridge_mode.c query_pcie_mode.c
FB_DHCP=udhcpc.c
FIBO_PROXY_SRC=fibo_qmimsg_server.c
LIBMNL=libmnl/ifutils.c libmnl/attr.c libmnl/callback.c libmnl/nlmsg.c libmnl/socket.c
FB_NDHCP=udhcpc_netlink.c
FB_NDHCP+=${LIBMNL}
release: clean
$(CC) $(CFLAGS) -Wall -s ${SRC} ${FB_NDHCP} -o fibocom-dial -lpthread -ldl
$(CC) -Wall -s multi-pdn-manager.c query_pcie_mode.c util.c -o multi-pdn-manager -lpthread -ldl
$(CC) -Wall -s ${FIBO_PROXY_SRC} -o fibo_qmimsg_server -lpthread -ldl
dhcp: clean
$(CC) $(CFLAGS) -Wall -s ${SRC} ${FB_DHCP} -o fibocom-dial -lpthread -ldl
$(CC) -Wall -s multi-pdn-manager.c query_pcie_mode.c util.c -o multi-pdn-manager -lpthread -ldl
$(CC) -Wall -s ${FIBO_PROXY_SRC} -o fibo_qmimsg_server -lpthread -ldl
ndhcp: clean
$(CC) $(CFLAGS) -Wall -s ${SRC} ${FB_NDHCP} -o fibocom-dial -lpthread -ldl
$(CC) -Wall -s multi-pdn-manager.c query_pcie_mode.cutil.c -o multi-pdn-manager -lpthread -ldl
$(CC) -Wall -s ${FIBO_PROXY_SRC} -o fibo_qmimsg_server -lpthread -ldl
qmi-proxy:
$(CC) -Wall -s fibo-qmi-proxy.c -o fibo-qmi-proxy -lpthread -ldl
clean:
rm -rf fibocom-dial *~ multi-pdn-manager fibo_qmimsg_server

2551
fibocom-dial/src/QMIThread.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,260 @@
#ifndef __QMI_THREAD_H__
#define __QMI_THREAD_H__
#define CONFIG_GOBINET
#define CONFIG_QMIWWAN
#define CONFIG_SIM
#define CONFIG_APN
#define CONFIG_VERSION
//2021-03-24 willa.liu@fibocom.com changed begin for support mantis 0071817
#define CONFIG_IMSI_ICCID
//2021-03-24 willa.liu@fibocom.com changed end for support mantis 0071817
#define CONFIG_DEFAULT_PDP 1
#define CONFIG_DEFAULT_PDPINDEX 1
//#define CONFIG_IMSI_ICCID
#define CONFIG_RESET_RADIO \
(45) // Reset Radiao(AT+CFUN=4,AT+CFUN=1) when cann not register network or
// setup data call in 45 seconds
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <pthread.h>
#include <signal.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/epoll.h>
#include <sys/ioctl.h>
#include <sys/poll.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <termios.h>
#include <time.h>
#include <unistd.h>
#include "MPQCTL.h"
#include "MPQMI.h"
#include "MPQMUX.h"
#define DEVICE_CLASS_UNKNOWN 0
#define DEVICE_CLASS_CDMA 1
#define DEVICE_CLASS_GSM 2
#define WWAN_DATA_CLASS_NONE 0x00000000
#define WWAN_DATA_CLASS_GPRS 0x00000001
#define WWAN_DATA_CLASS_EDGE 0x00000002 /* EGPRS */
#define WWAN_DATA_CLASS_UMTS 0x00000004
#define WWAN_DATA_CLASS_HSDPA 0x00000008
#define WWAN_DATA_CLASS_HSUPA 0x00000010
#define WWAN_DATA_CLASS_LTE 0x00000020
//begin modified by zhangkaibo add 5G network detect feature on x55 platform. 20200605
#define WWAN_DATA_CLASS_5G 0x00000040
//end modified by zhangkaibo add 5G network detect feature on x55 platform. 20200605
#define WWAN_DATA_CLASS_1XRTT 0x00010000
#define WWAN_DATA_CLASS_1XEVDO 0x00020000
#define WWAN_DATA_CLASS_1XEVDO_REVA 0x00040000
#define WWAN_DATA_CLASS_1XEVDV 0x00080000
#define WWAN_DATA_CLASS_3XRTT 0x00100000
#define WWAN_DATA_CLASS_1XEVDO_REVB 0x00200000 /* for future use */
#define WWAN_DATA_CLASS_UMB 0x00400000
#define WWAN_DATA_CLASS_CUSTOM 0x80000000
struct wwan_data_class_str {
ULONG class;
CHAR *str;
};
#pragma pack(push, 1)
typedef struct _QCQMIMSG {
QCQMI_HDR QMIHdr;
union {
QMICTL_MSG CTLMsg;
QMUX_MSG MUXMsg;
//2021-03-24 willa.liu@fibocom.com changed begin for support mantis 0071817
QMUX_MSG QMUXMsgHdrResp;
//2021-03-24 willa.liu@fibocom.com changed end for support mantis 0071817
};
} __attribute__((packed)) QCQMIMSG, *PQCQMIMSG;
#pragma pack(pop)
typedef struct __IPV4 {
uint32_t Address;
uint32_t Gateway;
uint32_t SubnetMask;
uint32_t DnsPrimary;
uint32_t DnsSecondary;
uint32_t Mtu;
} IPV4_T;
typedef struct __IPV6 {
UCHAR Address[16];
UCHAR Gateway[16];
UCHAR SubnetMask[16];
UCHAR DnsPrimary[16];
UCHAR DnsSecondary[16];
UCHAR PrefixLengthIPAddr;
UCHAR PrefixLengthGateway;
ULONG Mtu;
} IPV6_T;
#define IpFamilyV4 (0x04)
#define IpFamilyV6 (0x06)
struct __PROFILE;
struct qmi_device_ops {
int (*init)(struct __PROFILE *profile);
int (*deinit)(void);
int (*send)(PQCQMIMSG pRequest);
void *(*read)(void *pData);
};
extern int (*qmidev_send)(PQCQMIMSG pRequest);
#ifndef bool
#define bool uint8_t
#endif
//2021-03-15 zhangkaibo@fibocom.com changed begin for oa 20210311037
typedef struct {
unsigned int size;
unsigned int rx_urb_size;
unsigned int ep_type;
unsigned int iface_id;
unsigned int qmap_mode;
unsigned int qmap_version;
unsigned int dl_minimum_padding;
char ifname[8][16];
unsigned char mux_id[8];
} RMNET_INFO;
//2021-03-15 zhangkaibo@fibocom.com changed end for oa 20210311037
typedef struct __PROFILE {
char *qmichannel;
char *usbnet_adapter;
char *qmapnet_adapter;
const char *driver_name;
int qmap_mode;
int qmap_size;
int qmap_version;
const char *apn;
const char *user;
const char *password;
const char *pincode;
int auth;
int pdp;
int pdpindex;
int pdpnum;
int curIpFamily;
int rawIP;
int muxid;
IPV4_T ipv4;
IPV6_T ipv6;
int ipv4_flag;
int ipv6_flag;
//2021-02-25 willa.liu@fibocom.com changed begin for support eipd SN-20210129001
int ipv6_prigateway_flag;
//2021-02-25 willa.liu@fibocom.com changed begin for support eipd SN-20210129001
int dual_flag;
int apntype;
const struct qmi_device_ops *qmi_ops;
bool loopback_state;
int replication_factor;
//2021-02-08 zhangkaibo@fibocom.com changed begin for mantis 0070613
int interfacenum;
//2021-02-08 zhangkaibo@fibocom.com changed end for mantis 0070613
//2021-03-15 zhangkaibo@fibocom.com changed begin for oa 20210311037
RMNET_INFO rmnet_info;
//2021-03-15 zhangkaibo@fibocom.com changed end for oa 20210311037
} PROFILE_T;
typedef enum {
SIM_ABSENT = 0,
SIM_NOT_READY = 1,
SIM_READY =
2, /* SIM_READY means the radio state is RADIO_STATE_SIM_READY */
SIM_PIN = 3,
SIM_PUK = 4,
SIM_NETWORK_PERSONALIZATION = 5,
SIM_BAD = 6,
} SIM_Status;
//2021-03-24 willa.liu@fibocom.com changed begin for support mantis 0071817
typedef enum {
SIM_Card0 = 0,
SIM_Card1 = 1
} SIM_Select;
//2021-03-24 willa.liu@fibocom.com changed end for support mantis 0071817
#define WDM_DEFAULT_BUFSIZE 256
#define RIL_REQUEST_QUIT 0x1000
#define RIL_INDICATE_DEVICE_CONNECTED 0x1002
#define RIL_INDICATE_DEVICE_DISCONNECTED 0x1003
#define RIL_UNSOL_RESPONSE_VOICE_NETWORK_STATE_CHANGED 0x1004
#define RIL_UNSOL_DATA_CALL_LIST_CHANGED 0x1005
extern int pthread_cond_timeout_np(pthread_cond_t *cond, pthread_mutex_t *mutex,
unsigned msecs);
extern int QmiThreadSendQMI(PQCQMIMSG pRequest, PQCQMIMSG *ppResponse);
extern int QmiThreadSendQMITimeout(PQCQMIMSG pRequest, PQCQMIMSG *ppResponse,
unsigned msecs);
extern void QmiThreadRecvQMI(PQCQMIMSG pResponse);
extern int fibo_raw_ip_mode_check(const char *ifname);
extern void udhcpc_start(PROFILE_T *profile);
extern void udhcpc_stop(PROFILE_T *profile);
extern void udhcpc_start_pcie(PROFILE_T *profile);
extern void udhcpc_stop_pcie(PROFILE_T *profile);
extern void dump_qmi(void *dataBuffer, int dataLen);
extern void qmidevice_send_event_to_main(int triger_event);
extern int requestSetEthMode(PROFILE_T *profile);
//2021-03-24 willa.liu@fibocom.com changed begin for support mantis 0071817
//extern int requestGetSIMStatus(SIM_Status *pSIMStatus);
extern int requestGetSIMStatus(SIM_Status *pSIMStatus , const int sim_select);
//2021-03-24 willa.liu@fibocom.com changed end for support mantis 0071817
extern int requestEnterSimPin(const CHAR *pPinCode);
extern int requestGetICCID(void);
extern int requestGetIMSI(void);
extern int requestRegistrationState(UCHAR *pPSAttachedState);
extern int requestQueryDataCall(UCHAR *pConnectionStatus, int curIpFamily);
extern int requestSetupDataCall(PROFILE_T *profile, int curIpFamily);
extern int requestDeactivateDefaultPDP(PROFILE_T *profile, int curIpFamily);
extern int requestSetProfile(PROFILE_T *profile);
extern int requestGetProfile(PROFILE_T *profile);
extern int requestBaseBandVersion(const char **pp_reversion);
extern int requestGetIPAddress(PROFILE_T *profile, int curIpFamily);
extern int requestSetOperatingMode(UCHAR OperatingMode);
int requestRegistrationState2(UCHAR *pPSAttachedState);
extern int fibo_qmap_mode_set(PROFILE_T *profile);
extern int fibo_bridge_mode_detect(PROFILE_T *profile);
extern int fibo_qmap_mode_detect(PROFILE_T *profile);
extern const struct qmi_device_ops qmiwwan_qmidev_ops;
#define qmidev_is_gobinet(_qmichannel) \
(strncmp(_qmichannel, "/dev/qcqmi", strlen("/dev/qcqmi")) == 0)
#define qmidev_is_qmiwwan(_qmichannel) \
(strncmp(_qmichannel, "/dev/cdc-wdm", strlen("/dev/cdc-wdm")) == 0)
#define qmidev_is_pciemhi(_qmichannel) \
(strncmp(_qmichannel, "/dev/mhi_", strlen("/dev/mhi_")) == 0)
#define driver_is_qmi(_drv_name) \
(strncasecmp(_drv_name, "qmi_wwan", strlen("qmi_wwan")) == 0)
#define driver_is_mbim(_drv_name) \
(strncasecmp(_drv_name, "cdc_mbim", strlen("cdc_mbim")) == 0)
extern FILE *logfilefp;
extern int debug_qmi;
extern USHORT g_MobileCountryCode;
extern USHORT g_MobileNetworkCode;
extern int qmidevice_control_fd[2];
extern int qmiclientId[QMUX_TYPE_WDS_ADMIN + 1];
extern void dbg_time(const char *fmt, ...);
extern USHORT le16_to_cpu(USHORT v16);
extern UINT le32_to_cpu(UINT v32);
extern USHORT cpu_to_le16(USHORT v16);
extern UINT cpu_to_le32(UINT v32);
#endif

View File

@ -0,0 +1,426 @@
#include <ctype.h>
#include <stdio.h>
#include <string.h>
#include <termios.h>
typedef unsigned short sa_family_t;
#include <linux/un.h>
#include "QMIThread.h"
#ifdef CONFIG_QMIWWAN
#define FIBO_QMI_PROXY "fibo_qmimsg_server"
static int cdc_wdm_fd = -1;
static UCHAR GetQCTLTransactionId(void)
{
static int TransactionId = 0;
if (++TransactionId > 0xFF)
TransactionId = 1;
return TransactionId;
}
typedef USHORT (*CUSTOMQCTL)(PQMICTL_MSG pCTLMsg, void *arg);
static PQCQMIMSG ComposeQCTLMsg(USHORT QMICTLType,
CUSTOMQCTL customQctlMsgFunction, void *arg)
{
UCHAR QMIBuf[WDM_DEFAULT_BUFSIZE];
PQCQMIMSG pRequest = (PQCQMIMSG)QMIBuf;
int Length;
pRequest->QMIHdr.IFType = USB_CTL_MSG_TYPE_QMI;
pRequest->QMIHdr.CtlFlags = 0x00;
pRequest->QMIHdr.QMIType = QMUX_TYPE_CTL;
pRequest->QMIHdr.ClientId = 0x00;
pRequest->CTLMsg.QMICTLMsgHdr.CtlFlags = QMICTL_FLAG_REQUEST;
pRequest->CTLMsg.QMICTLMsgHdr.TransactionId = GetQCTLTransactionId();
pRequest->CTLMsg.QMICTLMsgHdr.QMICTLType = cpu_to_le16(QMICTLType);
if (customQctlMsgFunction)
pRequest->CTLMsg.QMICTLMsgHdr.Length =
cpu_to_le16(customQctlMsgFunction(&pRequest->CTLMsg, arg) -
sizeof(QCQMICTL_MSG_HDR));
else
pRequest->CTLMsg.QMICTLMsgHdr.Length = cpu_to_le16(0x0000);
pRequest->QMIHdr.Length =
cpu_to_le16(le16_to_cpu(pRequest->CTLMsg.QMICTLMsgHdr.Length) +
sizeof(QCQMICTL_MSG_HDR) + sizeof(QCQMI_HDR) - 1);
Length = le16_to_cpu(pRequest->QMIHdr.Length) + 1;
pRequest = (PQCQMIMSG)malloc(Length);
if (pRequest == NULL) {
dbg_time("%s fail to malloc", __func__);
} else {
memcpy(pRequest, QMIBuf, Length);
}
return pRequest;
}
static USHORT CtlGetVersionReq(PQMICTL_MSG QCTLMsg, void *arg)
{
QCTLMsg->GetVersionReq.TLVType = QCTLV_TYPE_REQUIRED_PARAMETER;
QCTLMsg->GetVersionReq.TLVLength = cpu_to_le16(0x0001);
QCTLMsg->GetVersionReq.QMUXTypes = QMUX_TYPE_ALL;
return sizeof(QMICTL_GET_VERSION_REQ_MSG);
}
static USHORT CtlGetClientIdReq(PQMICTL_MSG QCTLMsg, void *arg)
{
QCTLMsg->GetClientIdReq.TLVType = QCTLV_TYPE_REQUIRED_PARAMETER;
QCTLMsg->GetClientIdReq.TLVLength = cpu_to_le16(0x0001);
QCTLMsg->GetClientIdReq.QMIType = ((UCHAR *)arg)[0];
return sizeof(QMICTL_GET_CLIENT_ID_REQ_MSG);
}
static USHORT CtlReleaseClientIdReq(PQMICTL_MSG QCTLMsg, void *arg)
{
QCTLMsg->ReleaseClientIdReq.TLVType = QCTLV_TYPE_REQUIRED_PARAMETER;
QCTLMsg->ReleaseClientIdReq.TLVLength = cpu_to_le16(0x0002);
QCTLMsg->ReleaseClientIdReq.QMIType = ((UCHAR *)arg)[0];
QCTLMsg->ReleaseClientIdReq.ClientId = ((UCHAR *)arg)[1];
return sizeof(QMICTL_RELEASE_CLIENT_ID_REQ_MSG);
}
static int QmiWwanSendQMI(PQCQMIMSG pRequest)
{
struct pollfd pollfds[] = {{cdc_wdm_fd, POLLOUT, 0}};
int ret;
if (cdc_wdm_fd == -1) {
dbg_time("%s cdc_wdm_fd = -1", __func__);
return -ENODEV;
}
if (pRequest->QMIHdr.QMIType == QMUX_TYPE_WDS_IPV6)
pRequest->QMIHdr.QMIType = QMUX_TYPE_WDS;
do {
ret = poll(pollfds, sizeof(pollfds) / sizeof(pollfds[0]), 5000);
} while ((ret < 0) && (errno == EINTR));
if (pollfds[0].revents & POLLOUT) {
ssize_t nwrites = le16_to_cpu(pRequest->QMIHdr.Length) + 1;
ret = write(cdc_wdm_fd, pRequest, nwrites);
if (ret == nwrites) {
ret = 0;
} else {
dbg_time("%s write=%d, errno: %d (%s)", __func__, ret, errno,
strerror(errno));
}
} else {
dbg_time("%s poll=%d, revents = 0x%x, errno: %d (%s)", __func__, ret,
pollfds[0].revents, errno, strerror(errno));
}
return ret;
}
static int QmiWwanGetClientID(UCHAR QMIType)
{
PQCQMIMSG pResponse;
QmiThreadSendQMI(
ComposeQCTLMsg(QMICTL_GET_CLIENT_ID_REQ, CtlGetClientIdReq, &QMIType),
&pResponse);
if (pResponse) {
USHORT QMUXResult = cpu_to_le16(pResponse->CTLMsg.QMICTLMsgHdrRsp
.QMUXResult); // QMI_RESULT_SUCCESS
USHORT QMUXError = cpu_to_le16(pResponse->CTLMsg.QMICTLMsgHdrRsp
.QMUXError); // QMI_ERR_INVALID_ARG
// UCHAR QMIType = pResponse->CTLMsg.GetClientIdRsp.QMIType;
UCHAR ClientId = pResponse->CTLMsg.GetClientIdRsp.ClientId;
dbg_time("%s: QMIType = %d clientid %d", __func__, QMIType, ClientId);
if (!QMUXResult && !QMUXError &&
(QMIType == pResponse->CTLMsg.GetClientIdRsp.QMIType)) {
switch (QMIType) {
case QMUX_TYPE_WDS:
dbg_time("Get clientWDS = %d", ClientId);
break;
case QMUX_TYPE_DMS:
dbg_time("Get clientDMS = %d", ClientId);
break;
case QMUX_TYPE_NAS:
dbg_time("Get clientNAS = %d", ClientId);
break;
case QMUX_TYPE_QOS:
dbg_time("Get clientQOS = %d", ClientId);
break;
case QMUX_TYPE_WMS:
dbg_time("Get clientWMS = %d", ClientId);
break;
case QMUX_TYPE_PDS:
dbg_time("Get clientPDS = %d", ClientId);
break;
case QMUX_TYPE_UIM:
dbg_time("Get clientUIM = %d", ClientId);
break;
case QMUX_TYPE_WDS_ADMIN:
dbg_time("Get clientWDA = %d", ClientId);
break;
default:
break;
}
return ClientId;
}
}
return 0;
}
static int QmiWwanReleaseClientID(QMI_SERVICE_TYPE QMIType, UCHAR ClientId)
{
UCHAR argv[] = {QMIType, ClientId};
QmiThreadSendQMI(ComposeQCTLMsg(QMICTL_RELEASE_CLIENT_ID_REQ,
CtlReleaseClientIdReq, argv),
NULL);
return 0;
}
static int QmiWwanInit(PROFILE_T *profile)
{
unsigned i;
int ret;
PQCQMIMSG pResponse;
if (profile->qmap_mode == 0 || profile->qmap_mode == 1) {
for (i = 0; i < 10; i++) {
ret = QmiThreadSendQMITimeout(
ComposeQCTLMsg(QMICTL_SYNC_REQ, NULL, NULL), NULL, 1 * 1000);
if (!ret)
break;
sleep(1);
}
if (ret)
return ret;
}
QmiThreadSendQMI(
ComposeQCTLMsg(QMICTL_GET_VERSION_REQ, CtlGetVersionReq, NULL),
&pResponse);
if (profile->qmap_mode) {
if (pResponse) {
if (pResponse->CTLMsg.QMICTLMsgHdrRsp.QMUXResult == 0 &&
pResponse->CTLMsg.QMICTLMsgHdrRsp.QMUXError == 0) {
uint8_t NumElements = 0;
for (NumElements = 0;
NumElements < pResponse->CTLMsg.GetVersionRsp.NumElements;
NumElements++) {
if (pResponse->CTLMsg.GetVersionRsp.TypeVersion[NumElements]
.QMUXType == QMUX_TYPE_WDS_ADMIN)
profile->qmap_version = (pResponse->CTLMsg.GetVersionRsp
.TypeVersion[NumElements]
.MinorVersion > 16);
}
}
}
}
if (pResponse)
free(pResponse);
if (profile->ipv4_flag)
qmiclientId[QMUX_TYPE_WDS] = QmiWwanGetClientID(QMUX_TYPE_WDS);
if (profile->ipv6_flag)
qmiclientId[QMUX_TYPE_WDS_IPV6] = QmiWwanGetClientID(QMUX_TYPE_WDS);
qmiclientId[QMUX_TYPE_DMS] = QmiWwanGetClientID(QMUX_TYPE_DMS);
qmiclientId[QMUX_TYPE_NAS] = QmiWwanGetClientID(QMUX_TYPE_NAS);
qmiclientId[QMUX_TYPE_UIM] = QmiWwanGetClientID(QMUX_TYPE_UIM);
if (profile->qmap_mode == 0 || profile->qmap_mode == 1)
qmiclientId[QMUX_TYPE_WDS_ADMIN] =
QmiWwanGetClientID(QMUX_TYPE_WDS_ADMIN);
return 0;
}
static int QmiWwanDeInit(void)
{
unsigned int i;
for (i = 0; i < sizeof(qmiclientId) / sizeof(qmiclientId[0]); i++) {
if (qmiclientId[i] != 0) {
QmiWwanReleaseClientID(i, qmiclientId[i]);
qmiclientId[i] = 0;
}
}
return 0;
}
static int qmi_proxy_open(const char *name)
{
int sockfd = -1;
int reuse_addr = 1;
struct sockaddr_un sockaddr;
socklen_t alen;
/*Create server socket*/
(sockfd = socket(AF_LOCAL, SOCK_STREAM, 0));
if (sockfd < 0)
return sockfd;
memset(&sockaddr, 0, sizeof(sockaddr));
sockaddr.sun_family = AF_LOCAL;
sockaddr.sun_path[0] = 0;
memcpy(sockaddr.sun_path + 1, name, strlen(name));
alen = strlen(name) + offsetof(struct sockaddr_un, sun_path) + 1;
if (connect(sockfd, (struct sockaddr *)&sockaddr, alen) < 0) {
close(sockfd);
dbg_time("%s connect %s errno: %d (%s)\n", __func__, name, errno,
strerror(errno));
return -1;
}
(setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &reuse_addr,
sizeof(reuse_addr)));
dbg_time("connect to %s sockfd = %d\n", name, sockfd);
return sockfd;
}
static ssize_t qmi_proxy_read(int fd, void *buf, size_t size)
{
ssize_t nreads;
PQCQMI_HDR pHdr = (PQCQMI_HDR)buf;
nreads = read(fd, pHdr, sizeof(QCQMI_HDR));
if (nreads == sizeof(QCQMI_HDR)) {
nreads += read(fd, pHdr + 1,
le16_to_cpu(pHdr->Length) + 1 - sizeof(QCQMI_HDR));
}
return nreads;
}
static void *QmiWwanThread(void *pData)
{
PROFILE_T *profile = (PROFILE_T *)pData;
const char *cdc_wdm = (const char *)profile->qmichannel;
if (profile->qmap_mode == 0 || profile->qmap_mode == 1)
cdc_wdm_fd = open(cdc_wdm, O_RDWR | O_NONBLOCK | O_NOCTTY);
else
cdc_wdm_fd = qmi_proxy_open(FIBO_QMI_PROXY);
if (cdc_wdm_fd == -1) {
dbg_time("%s Failed to open %s, errno: %d (%s)", __func__, cdc_wdm,
errno, strerror(errno));
qmidevice_send_event_to_main(RIL_INDICATE_DEVICE_DISCONNECTED);
pthread_exit(NULL);
return NULL;
}
fcntl(cdc_wdm_fd, F_SETFL, fcntl(cdc_wdm_fd, F_GETFL) | O_NONBLOCK);
fcntl(cdc_wdm_fd, F_SETFD, FD_CLOEXEC);
dbg_time("cdc_wdm_fd = %d", cdc_wdm_fd);
qmidevice_send_event_to_main(RIL_INDICATE_DEVICE_CONNECTED);
while (1) {
struct pollfd pollfds[] = {{qmidevice_control_fd[1], POLLIN, 0},
{cdc_wdm_fd, POLLIN, 0}};
int ne, ret, nevents = sizeof(pollfds) / sizeof(pollfds[0]);
do {
ret = poll(pollfds, nevents, -1);
} while ((ret < 0) && (errno == EINTR));
if (ret <= 0) {
dbg_time("%s poll=%d, errno: %d (%s)", __func__, ret, errno,
strerror(errno));
break;
}
for (ne = 0; ne < nevents; ne++) {
int fd = pollfds[ne].fd;
short revents = pollfds[ne].revents;
// dbg_time("{%d, %x, %x}", pollfds[ne].fd, pollfds[ne].events,
// pollfds[ne].revents);
if (revents & (POLLERR | POLLHUP | POLLNVAL)) {
dbg_time("%s poll err/hup/inval", __func__);
dbg_time("poll fd = %d, events = 0x%04x", fd, revents);
if (fd == cdc_wdm_fd) {
} else {
}
if (revents &
(POLLHUP | POLLNVAL)) // EC20 bug, Can get POLLERR
goto __QmiWwanThread_quit;
}
if ((revents & POLLIN) == 0)
continue;
if (fd == qmidevice_control_fd[1]) {
int triger_event;
if (read(fd, &triger_event, sizeof(triger_event)) ==
sizeof(triger_event)) {
// DBG("triger_event = 0x%x", triger_event);
switch (triger_event) {
case RIL_REQUEST_QUIT:
goto __QmiWwanThread_quit;
break;
case SIGTERM:
case SIGHUP:
case SIGINT:
QmiThreadRecvQMI(NULL);
break;
default:
break;
}
}
}
if (fd == cdc_wdm_fd) {
ssize_t nreads;
UCHAR QMIBuf[4096];
PQCQMIMSG pResponse = (PQCQMIMSG)QMIBuf;
if (profile->qmap_mode == 0 || profile->qmap_mode == 1)
nreads = read(fd, QMIBuf, sizeof(QMIBuf));
else
nreads = qmi_proxy_read(fd, QMIBuf, sizeof(QMIBuf));
// dbg_time("%s read=%d errno: %d (%s)", __func__, (int)nreads,
// errno, strerror(errno));
if (nreads <= 0) {
dbg_time("%s read=%d errno: %d (%s)", __func__, (int)nreads,
errno, strerror(errno));
break;
}
if (nreads != (le16_to_cpu(pResponse->QMIHdr.Length) + 1)) {
dbg_time("%s nreads=%d, pQCQMI->QMIHdr.Length = %d",
__func__, (int)nreads,
le16_to_cpu(pResponse->QMIHdr.Length));
continue;
}
QmiThreadRecvQMI(pResponse);
}
}
}
__QmiWwanThread_quit:
if (cdc_wdm_fd != -1) {
close(cdc_wdm_fd);
cdc_wdm_fd = -1;
}
qmidevice_send_event_to_main(RIL_INDICATE_DEVICE_DISCONNECTED);
QmiThreadRecvQMI(NULL); // main thread may pending on QmiThreadSendQMI()
dbg_time("%s exit", __func__);
pthread_exit(NULL);
return NULL;
}
#endif
const struct qmi_device_ops qmiwwan_qmidev_ops = {
.init = QmiWwanInit,
.deinit = QmiWwanDeInit,
.send = QmiWwanSendQMI,
.read = QmiWwanThread,
};

View File

@ -0,0 +1,63 @@
#!/bin/sh
# Busybox udhcpc dispatcher script. Copyright (C) 2009 by Axel Beckert.
#
# Based on the busybox example scripts and the old udhcp source
# package default.* scripts.
RESOLV_CONF="/etc/resolv.conf"
case $1 in
bound|renew)
[ -n "$broadcast" ] && BROADCAST="broadcast $broadcast"
[ -n "$subnet" ] && NETMASK="netmask $subnet"
/sbin/ifconfig $interface $ip $BROADCAST $NETMASK
if [ -n "$router" ]; then
echo "$0: Resetting default routes"
while /sbin/route del default gw 0.0.0.0 dev $interface; do :; done
metric=0
for i in $router; do
/sbin/route add default gw $i dev $interface metric $metric
metric=$(($metric + 1))
done
fi
# Update resolver configuration file
R=""
[ -n "$domain" ] && R="domain $domain
"
for i in $dns; do
echo "$0: Adding DNS $i"
R="${R}nameserver $i
"
done
if [ -x /sbin/resolvconf ]; then
echo -n "$R" | resolvconf -a "${interface}.udhcpc"
else
echo -n "$R" > "$RESOLV_CONF"
fi
;;
deconfig)
if [ -x /sbin/resolvconf ]; then
resolvconf -d "${interface}.udhcpc"
fi
/sbin/ifconfig $interface 0.0.0.0
;;
leasefail)
echo "$0: Lease failed: $message"
;;
nak)
echo "$0: Received a NAK: $message"
;;
*)
echo "$0: Unknown udhcpc command: $1";
exit 1;
;;
esac

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,28 @@
= What is libmnl? =
libmnl is a minimalistic user-space library oriented to Netlink developers.
There are a lot of common tasks in parsing, validating, constructing of
both the Netlink header and TLVs that are repetitive and easy to get wrong.
This library aims to provide simple helpers that allows you to re-use code
and to avoid re-inventing the wheel. The main features of this library are:
* Small: the shared library requires around 30KB for an x86-based computer.
* Simple: this library avoids complexity and elaborated abstractions that
tend to hide Netlink details.
* Easy to use: the library simplifies the work for Netlink-wise developers.
It provides functions to make socket handling, message building, validating,
parsing and sequence tracking, easier.
* Easy to re-use: you can use the library to build your own abstraction layer
on top of this library.
* Decoupling: the interdependency of the main bricks that compose the library
is reduced, i.e. the library provides many helpers, but the programmer is not
forced to use them.
= Example files =
You can find several example files under examples/ that you can compile by
invoking `make check'.
--
08/sep/2010
Pablo Neira Ayuso <pablo@netfilter.org>

View File

@ -0,0 +1,722 @@
/*
* (C) 2008-2012 by Pablo Neira Ayuso <pablo@netfilter.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*/
#include <limits.h> /* for INT_MAX */
#include <string.h>
#include <errno.h>
#include "libmnl.h"
/**
* \defgroup attr Netlink attribute helpers
*
* Netlink Type-Length-Value (TLV) attribute:
* \verbatim
|<-- 2 bytes -->|<-- 2 bytes -->|<-- variable -->|
-------------------------------------------------
| length | type | value |
-------------------------------------------------
|<--------- header ------------>|<-- payload --->|
\endverbatim
* The payload of the Netlink message contains sequences of attributes that are
* expressed in TLV format.
*
* @{
*/
/**
* mnl_attr_get_type - get type of netlink attribute
* \param attr pointer to netlink attribute
*
* This function returns the attribute type.
*/
uint16_t mnl_attr_get_type(const struct nlattr *attr)
{
return attr->nla_type & NLA_TYPE_MASK;
}
/**
* mnl_attr_get_len - get length of netlink attribute
* \param attr pointer to netlink attribute
*
* This function returns the attribute length that is the attribute header
* plus the attribute payload.
*/
uint16_t mnl_attr_get_len(const struct nlattr *attr)
{
return attr->nla_len;
}
/**
* mnl_attr_get_payload_len - get the attribute payload-value length
* \param attr pointer to netlink attribute
*
* This function returns the attribute payload-value length.
*/
uint16_t mnl_attr_get_payload_len(const struct nlattr *attr)
{
return attr->nla_len - MNL_ATTR_HDRLEN;
}
/**
* mnl_attr_get_payload - get pointer to the attribute payload
* \param attr pointer to netlink attribute
*
* This function return a pointer to the attribute payload.
*/
void *mnl_attr_get_payload(const struct nlattr *attr)
{
return (void *)attr + MNL_ATTR_HDRLEN;
}
/**
* mnl_attr_ok - check if there is room for an attribute in a buffer
* \param attr attribute that we want to check if there is room for
* \param len remaining bytes in a buffer that contains the attribute
*
* This function is used to check that a buffer, which is supposed to contain
* an attribute, has enough room for the attribute that it stores, i.e. this
* function can be used to verify that an attribute is neither malformed nor
* truncated.
*
* This function does not set errno in case of error since it is intended
* for iterations. Thus, it returns true on success and false on error.
*
* The len parameter may be negative in the case of malformed messages during
* attribute iteration, that is why we use a signed integer.
*/
bool mnl_attr_ok(const struct nlattr *attr, int len)
{
return len >= (int)sizeof(struct nlattr) &&
attr->nla_len >= sizeof(struct nlattr) &&
(int)attr->nla_len <= len;
}
/**
* mnl_attr_next - get the next attribute in the payload of a netlink message
* \param attr pointer to the current attribute
*
* This function returns a pointer to the next attribute after the one passed
* as parameter. You have to use mnl_attr_ok() to ensure that the next
* attribute is valid.
*/
struct nlattr *mnl_attr_next(const struct nlattr *attr)
{
return (struct nlattr *)((void *)attr + MNL_ALIGN(attr->nla_len));
}
/**
* mnl_attr_type_valid - check if the attribute type is valid
* \param attr pointer to attribute to be checked
* \param max maximum attribute type
*
* This function allows to check if the attribute type is higher than the
* maximum supported type. If the attribute type is invalid, this function
* returns -1 and errno is explicitly set. On success, this function returns 1.
*
* Strict attribute checking in user-space is not a good idea since you may
* run an old application with a newer kernel that supports new attributes.
* This leads to backward compatibility breakages in user-space. Better check
* if you support an attribute, if not, skip it.
*/
int mnl_attr_type_valid(const struct nlattr *attr, uint16_t max)
{
if (mnl_attr_get_type(attr) > max) {
errno = EOPNOTSUPP;
return -1;
}
return 1;
}
static int __mnl_attr_validate(const struct nlattr *attr,
enum mnl_attr_data_type type, size_t exp_len)
{
uint16_t attr_len = mnl_attr_get_payload_len(attr);
const char *attr_data = mnl_attr_get_payload(attr);
if (attr_len < exp_len) {
errno = ERANGE;
return -1;
}
switch(type) {
case MNL_TYPE_FLAG:
if (attr_len > 0) {
errno = ERANGE;
return -1;
}
break;
case MNL_TYPE_NUL_STRING:
if (attr_len == 0) {
errno = ERANGE;
return -1;
}
if (attr_data[attr_len-1] != '\0') {
errno = EINVAL;
return -1;
}
break;
case MNL_TYPE_STRING:
if (attr_len == 0) {
errno = ERANGE;
return -1;
}
break;
case MNL_TYPE_NESTED:
/* empty nested attributes are OK. */
if (attr_len == 0)
break;
/* if not empty, they must contain one header, eg. flag */
if (attr_len < MNL_ATTR_HDRLEN) {
errno = ERANGE;
return -1;
}
break;
default:
/* make gcc happy. */
break;
}
if (exp_len && attr_len > exp_len) {
errno = ERANGE;
return -1;
}
return 0;
}
static const size_t mnl_attr_data_type_len[MNL_TYPE_MAX] = {
[MNL_TYPE_U8] = sizeof(uint8_t),
[MNL_TYPE_U16] = sizeof(uint16_t),
[MNL_TYPE_U32] = sizeof(uint32_t),
[MNL_TYPE_U64] = sizeof(uint64_t),
[MNL_TYPE_MSECS] = sizeof(uint64_t),
};
/**
* mnl_attr_validate - validate netlink attribute (simplified version)
* \param attr pointer to netlink attribute that we want to validate
* \param type data type (see enum mnl_attr_data_type)
*
* The validation is based on the data type. Specifically, it checks that
* integers (u8, u16, u32 and u64) have enough room for them. This function
* returns -1 in case of error, and errno is explicitly set.
*/
int mnl_attr_validate(const struct nlattr *attr, enum mnl_attr_data_type type)
{
int exp_len;
if (type >= MNL_TYPE_MAX) {
errno = EINVAL;
return -1;
}
exp_len = mnl_attr_data_type_len[type];
return __mnl_attr_validate(attr, type, exp_len);
}
/**
* mnl_attr_validate2 - validate netlink attribute (extended version)
* \param attr pointer to netlink attribute that we want to validate
* \param type attribute type (see enum mnl_attr_data_type)
* \param exp_len expected attribute data size
*
* This function allows to perform a more accurate validation for attributes
* whose size is variable. If the size of the attribute is not what we expect,
* this functions returns -1 and errno is explicitly set.
*/
int mnl_attr_validate2(const struct nlattr *attr,
enum mnl_attr_data_type type,
size_t exp_len)
{
if (type >= MNL_TYPE_MAX) {
errno = EINVAL;
return -1;
}
return __mnl_attr_validate(attr, type, exp_len);
}
/**
* mnl_attr_parse - parse attributes
* \param nlh pointer to netlink message
* \param offset offset to start parsing from (if payload is after any header)
* \param cb callback function that is called for each attribute
* \param data pointer to data that is passed to the callback function
*
* This function allows to iterate over the sequence of attributes that compose
* the Netlink message. You can then put the attribute in an array as it
* usually happens at this stage or you can use any other data structure (such
* as lists or trees).
*
* This function propagates the return value of the callback, which can be
* MNL_CB_ERROR, MNL_CB_OK or MNL_CB_STOP.
*/
int mnl_attr_parse(const struct nlmsghdr *nlh,
unsigned int offset, mnl_attr_cb_t cb,
void *data)
{
int ret = MNL_CB_OK;
const struct nlattr *attr;
mnl_attr_for_each(attr, nlh, offset)
if ((ret = cb(attr, data)) <= MNL_CB_STOP)
return ret;
return ret;
}
/**
* mnl_attr_parse_nested - parse attributes inside a nest
* \param nested pointer to netlink attribute that contains a nest
* \param cb callback function that is called for each attribute in the nest
* \param data pointer to data passed to the callback function
*
* This function allows to iterate over the sequence of attributes that compose
* the Netlink message. You can then put the attribute in an array as it
* usually happens at this stage or you can use any other data structure (such
* as lists or trees).
*
* This function propagates the return value of the callback, which can be
* MNL_CB_ERROR, MNL_CB_OK or MNL_CB_STOP.
*/
int mnl_attr_parse_nested(const struct nlattr *nested,
mnl_attr_cb_t cb, void *data)
{
int ret = MNL_CB_OK;
const struct nlattr *attr;
mnl_attr_for_each_nested(attr, nested)
if ((ret = cb(attr, data)) <= MNL_CB_STOP)
return ret;
return ret;
}
/**
* mnl_attr_parse_payload - parse attributes in payload of Netlink message
* \param payload pointer to payload of the Netlink message
* \param payload_len payload length that contains the attributes
* \param cb callback function that is called for each attribute
* \param data pointer to data that is passed to the callback function
*
* This function takes a pointer to the area that contains the attributes,
* commonly known as the payload of the Netlink message. Thus, you have to
* pass a pointer to the Netlink message payload, instead of the entire
* message.
*
* This function allows you to iterate over the sequence of attributes that are
* located at some payload offset. You can then put the attributes in one array
* as usual, or you can use any other data structure (such as lists or trees).
*
* This function propagates the return value of the callback, which can be
* MNL_CB_ERROR, MNL_CB_OK or MNL_CB_STOP.
*/
int mnl_attr_parse_payload(const void *payload,
size_t payload_len,
mnl_attr_cb_t cb, void *data)
{
int ret = MNL_CB_OK;
const struct nlattr *attr;
mnl_attr_for_each_payload(payload, payload_len)
if ((ret = cb(attr, data)) <= MNL_CB_STOP)
return ret;
return ret;
}
/**
* mnl_attr_get_u8 - returns 8-bit unsigned integer attribute payload
* \param attr pointer to netlink attribute
*
* This function returns the 8-bit value of the attribute payload.
*/
uint8_t mnl_attr_get_u8(const struct nlattr *attr)
{
return *((uint8_t *)mnl_attr_get_payload(attr));
}
/**
* mnl_attr_get_u16 - returns 16-bit unsigned integer attribute payload
* \param attr pointer to netlink attribute
*
* This function returns the 16-bit value of the attribute payload.
*/
uint16_t mnl_attr_get_u16(const struct nlattr *attr)
{
return *((uint16_t *)mnl_attr_get_payload(attr));
}
/**
* mnl_attr_get_u32 - returns 32-bit unsigned integer attribute payload
* \param attr pointer to netlink attribute
*
* This function returns the 32-bit value of the attribute payload.
*/
uint32_t mnl_attr_get_u32(const struct nlattr *attr)
{
return *((uint32_t *)mnl_attr_get_payload(attr));
}
/**
* mnl_attr_get_u64 - returns 64-bit unsigned integer attribute.
* \param attr pointer to netlink attribute
*
* This function returns the 64-bit value of the attribute payload. This
* function is align-safe, since accessing 64-bit Netlink attributes is a
* common source of alignment issues.
*/
uint64_t mnl_attr_get_u64(const struct nlattr *attr)
{
uint64_t tmp;
memcpy(&tmp, mnl_attr_get_payload(attr), sizeof(tmp));
return tmp;
}
/**
* mnl_attr_get_str - returns pointer to string attribute.
* \param attr pointer to netlink attribute
*
* This function returns the payload of string attribute value.
*/
const char *mnl_attr_get_str(const struct nlattr *attr)
{
return mnl_attr_get_payload(attr);
}
/**
* mnl_attr_put - add an attribute to netlink message
* \param nlh pointer to the netlink message
* \param type netlink attribute type that you want to add
* \param len netlink attribute payload length
* \param data pointer to the data that will be stored by the new attribute
*
* This function updates the length field of the Netlink message (nlmsg_len)
* by adding the size (header + payload) of the new attribute.
*/
void mnl_attr_put(struct nlmsghdr *nlh, uint16_t type,
size_t len, const void *data)
{
struct nlattr *attr = mnl_nlmsg_get_payload_tail(nlh);
uint16_t payload_len = MNL_ALIGN(sizeof(struct nlattr)) + len;
int pad;
attr->nla_type = type;
attr->nla_len = payload_len;
memcpy(mnl_attr_get_payload(attr), data, len);
pad = MNL_ALIGN(len) - len;
if (pad > 0)
memset(mnl_attr_get_payload(attr) + len, 0, pad);
nlh->nlmsg_len += MNL_ALIGN(payload_len);
}
/**
* mnl_attr_put_u8 - add 8-bit unsigned integer attribute to netlink message
* \param nlh pointer to the netlink message
* \param type netlink attribute type
* \param data 8-bit unsigned integer data that is stored by the new attribute
*
* This function updates the length field of the Netlink message (nlmsg_len)
* by adding the size (header + payload) of the new attribute.
*/
void mnl_attr_put_u8(struct nlmsghdr *nlh, uint16_t type,
uint8_t data)
{
mnl_attr_put(nlh, type, sizeof(uint8_t), &data);
}
/**
* mnl_attr_put_u16 - add 16-bit unsigned integer attribute to netlink message
* \param nlh pointer to the netlink message
* \param type netlink attribute type
* \param data 16-bit unsigned integer data that is stored by the new attribute
*
* This function updates the length field of the Netlink message (nlmsg_len)
* by adding the size (header + payload) of the new attribute.
*/
void mnl_attr_put_u16(struct nlmsghdr *nlh, uint16_t type,
uint16_t data)
{
mnl_attr_put(nlh, type, sizeof(uint16_t), &data);
}
/**
* mnl_attr_put_u32 - add 32-bit unsigned integer attribute to netlink message
* \param nlh pointer to the netlink message
* \param type netlink attribute type
* \param data 32-bit unsigned integer data that is stored by the new attribute
*
* This function updates the length field of the Netlink message (nlmsg_len)
* by adding the size (header + payload) of the new attribute.
*/
void mnl_attr_put_u32(struct nlmsghdr *nlh, uint16_t type,
uint32_t data)
{
mnl_attr_put(nlh, type, sizeof(uint32_t), &data);
}
/**
* mnl_attr_put_u64 - add 64-bit unsigned integer attribute to netlink message
* \param nlh pointer to the netlink message
* \param type netlink attribute type
* \param data 64-bit unsigned integer data that is stored by the new attribute
*
* This function updates the length field of the Netlink message (nlmsg_len)
* by adding the size (header + payload) of the new attribute.
*/
void mnl_attr_put_u64(struct nlmsghdr *nlh, uint16_t type,
uint64_t data)
{
mnl_attr_put(nlh, type, sizeof(uint64_t), &data);
}
/**
* mnl_attr_put_str - add string attribute to netlink message
* \param nlh pointer to the netlink message
* \param type netlink attribute type
* \param data pointer to string data that is stored by the new attribute
*
* This function updates the length field of the Netlink message (nlmsg_len)
* by adding the size (header + payload) of the new attribute.
*/
void mnl_attr_put_str(struct nlmsghdr *nlh, uint16_t type,
const char *data)
{
mnl_attr_put(nlh, type, strlen(data), data);
}
/**
* mnl_attr_put_strz - add string attribute to netlink message
* \param nlh pointer to the netlink message
* \param type netlink attribute type
* \param data pointer to string data that is stored by the new attribute
*
* This function is similar to mnl_attr_put_str, but it includes the
* NUL/zero ('\0') terminator at the end of the string.
*
* This function updates the length field of the Netlink message (nlmsg_len)
* by adding the size (header + payload) of the new attribute.
*/
void mnl_attr_put_strz(struct nlmsghdr *nlh, uint16_t type,
const char *data)
{
mnl_attr_put(nlh, type, strlen(data)+1, data);
}
/**
* mnl_attr_nest_start - start an attribute nest
* \param nlh pointer to the netlink message
* \param type netlink attribute type
*
* This function adds the attribute header that identifies the beginning of
* an attribute nest. This function always returns a valid pointer to the
* beginning of the nest.
*/
struct nlattr *mnl_attr_nest_start(struct nlmsghdr *nlh,
uint16_t type)
{
struct nlattr *start = mnl_nlmsg_get_payload_tail(nlh);
/* set start->nla_len in mnl_attr_nest_end() */
start->nla_type = NLA_F_NESTED | type;
nlh->nlmsg_len += MNL_ALIGN(sizeof(struct nlattr));
return start;
}
/**
* mnl_attr_put_check - add an attribute to netlink message
* \param nlh pointer to the netlink message
* \param buflen size of buffer which stores the message
* \param type netlink attribute type that you want to add
* \param len netlink attribute payload length
* \param data pointer to the data that will be stored by the new attribute
*
* This function first checks that the data can be added to the message
* (fits into the buffer) and then updates the length field of the Netlink
* message (nlmsg_len) by adding the size (header + payload) of the new
* attribute. The function returns true if the attribute could be added
* to the message, otherwise false is returned.
*/
bool mnl_attr_put_check(struct nlmsghdr *nlh, size_t buflen,
uint16_t type, size_t len,
const void *data)
{
if (nlh->nlmsg_len + MNL_ATTR_HDRLEN + MNL_ALIGN(len) > buflen)
return false;
mnl_attr_put(nlh, type, len, data);
return true;
}
/**
* mnl_attr_put_u8_check - add 8-bit unsigned int attribute to netlink message
* \param nlh pointer to the netlink message
* \param buflen size of buffer which stores the message
* \param type netlink attribute type
* \param data 8-bit unsigned integer data that is stored by the new attribute
*
* This function first checks that the data can be added to the message
* (fits into the buffer) and then updates the length field of the Netlink
* message (nlmsg_len) by adding the size (header + payload) of the new
* attribute. The function returns true if the attribute could be added
* to the message, otherwise false is returned.
*/
bool mnl_attr_put_u8_check(struct nlmsghdr *nlh, size_t buflen,
uint16_t type, uint8_t data)
{
return mnl_attr_put_check(nlh, buflen, type, sizeof(uint8_t), &data);
}
/**
* mnl_attr_put_u16_check - add 16-bit unsigned int attribute to netlink message
* \param nlh pointer to the netlink message
* \param buflen size of buffer which stores the message
* \param type netlink attribute type
* \param data 16-bit unsigned integer data that is stored by the new attribute
*
* This function first checks that the data can be added to the message
* (fits into the buffer) and then updates the length field of the Netlink
* message (nlmsg_len) by adding the size (header + payload) of the new
* attribute. The function returns true if the attribute could be added
* to the message, otherwise false is returned.
* This function updates the length field of the Netlink message (nlmsg_len)
* by adding the size (header + payload) of the new attribute.
*/
bool mnl_attr_put_u16_check(struct nlmsghdr *nlh, size_t buflen,
uint16_t type, uint16_t data)
{
return mnl_attr_put_check(nlh, buflen, type, sizeof(uint16_t), &data);
}
/**
* mnl_attr_put_u32_check - add 32-bit unsigned int attribute to netlink message
* \param nlh pointer to the netlink message
* \param buflen size of buffer which stores the message
* \param type netlink attribute type
* \param data 32-bit unsigned integer data that is stored by the new attribute
*
* This function first checks that the data can be added to the message
* (fits into the buffer) and then updates the length field of the Netlink
* message (nlmsg_len) by adding the size (header + payload) of the new
* attribute. The function returns true if the attribute could be added
* to the message, otherwise false is returned.
* This function updates the length field of the Netlink message (nlmsg_len)
* by adding the size (header + payload) of the new attribute.
*/
bool mnl_attr_put_u32_check(struct nlmsghdr *nlh, size_t buflen,
uint16_t type, uint32_t data)
{
return mnl_attr_put_check(nlh, buflen, type, sizeof(uint32_t), &data);
}
/**
* mnl_attr_put_u64_check - add 64-bit unsigned int attribute to netlink message
* \param nlh pointer to the netlink message
* \param buflen size of buffer which stores the message
* \param type netlink attribute type
* \param data 64-bit unsigned integer data that is stored by the new attribute
*
* This function first checks that the data can be added to the message
* (fits into the buffer) and then updates the length field of the Netlink
* message (nlmsg_len) by adding the size (header + payload) of the new
* attribute. The function returns true if the attribute could be added
* to the message, otherwise false is returned.
* This function updates the length field of the Netlink message (nlmsg_len)
* by adding the size (header + payload) of the new attribute.
*/
bool mnl_attr_put_u64_check(struct nlmsghdr *nlh, size_t buflen,
uint16_t type, uint64_t data)
{
return mnl_attr_put_check(nlh, buflen, type, sizeof(uint64_t), &data);
}
/**
* mnl_attr_put_str_check - add string attribute to netlink message
* \param nlh pointer to the netlink message
* \param buflen size of buffer which stores the message
* \param type netlink attribute type
* \param data pointer to string data that is stored by the new attribute
*
* This function first checks that the data can be added to the message
* (fits into the buffer) and then updates the length field of the Netlink
* message (nlmsg_len) by adding the size (header + payload) of the new
* attribute. The function returns true if the attribute could be added
* to the message, otherwise false is returned.
* This function updates the length field of the Netlink message (nlmsg_len)
* by adding the size (header + payload) of the new attribute.
*/
bool mnl_attr_put_str_check(struct nlmsghdr *nlh, size_t buflen,
uint16_t type, const char *data)
{
return mnl_attr_put_check(nlh, buflen, type, strlen(data), data);
}
/**
* mnl_attr_put_strz_check - add string attribute to netlink message
* \param nlh pointer to the netlink message
* \param buflen size of buffer which stores the message
* \param type netlink attribute type
* \param data pointer to string data that is stored by the new attribute
*
* This function is similar to mnl_attr_put_str, but it includes the
* NUL/zero ('\0') terminator at the end of the string.
*
* This function first checks that the data can be added to the message
* (fits into the buffer) and then updates the length field of the Netlink
* message (nlmsg_len) by adding the size (header + payload) of the new
* attribute. The function returns true if the attribute could be added
* to the message, otherwise false is returned.
*/
bool mnl_attr_put_strz_check(struct nlmsghdr *nlh, size_t buflen,
uint16_t type, const char *data)
{
return mnl_attr_put_check(nlh, buflen, type, strlen(data)+1, data);
}
/**
* mnl_attr_nest_start_check - start an attribute nest
* \param buflen size of buffer which stores the message
* \param nlh pointer to the netlink message
* \param type netlink attribute type
*
* This function adds the attribute header that identifies the beginning of
* an attribute nest. If the nested attribute cannot be added then NULL,
* otherwise valid pointer to the beginning of the nest is returned.
*/
struct nlattr *mnl_attr_nest_start_check(struct nlmsghdr *nlh,
size_t buflen,
uint16_t type)
{
if (nlh->nlmsg_len + MNL_ATTR_HDRLEN > buflen)
return NULL;
return mnl_attr_nest_start(nlh, type);
}
/**
* mnl_attr_nest_end - end an attribute nest
* \param nlh pointer to the netlink message
* \param start pointer to the attribute nest returned by mnl_attr_nest_start()
*
* This function updates the attribute header that identifies the nest.
*/
void mnl_attr_nest_end(struct nlmsghdr *nlh,
struct nlattr *start)
{
start->nla_len = mnl_nlmsg_get_payload_tail(nlh) - (void *)start;
}
/**
* mnl_attr_nest_cancel - cancel an attribute nest
* \param nlh pointer to the netlink message
* \param start pointer to the attribute nest returned by mnl_attr_nest_start()
*
* This function updates the attribute header that identifies the nest.
*/
void mnl_attr_nest_cancel(struct nlmsghdr *nlh,
struct nlattr *start)
{
nlh->nlmsg_len -= mnl_nlmsg_get_payload_tail(nlh) - (void *)start;
}
/**
* @}
*/

View File

@ -0,0 +1,167 @@
/*
* (C) 2008-2010 by Pablo Neira Ayuso <pablo@netfilter.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*/
#include <errno.h>
#include "libmnl.h"
static int mnl_cb_noop(const struct nlmsghdr *nlh, void *data)
{
return MNL_CB_OK;
}
static int mnl_cb_error(const struct nlmsghdr *nlh, void *data)
{
const struct nlmsgerr *err = mnl_nlmsg_get_payload(nlh);
if (nlh->nlmsg_len < mnl_nlmsg_size(sizeof(struct nlmsgerr))) {
errno = EBADMSG;
return MNL_CB_ERROR;
}
/* Netlink subsystems returns the errno value with different signess */
if (err->error < 0)
errno = -err->error;
else
errno = err->error;
return err->error == 0 ? MNL_CB_STOP : MNL_CB_ERROR;
}
static int mnl_cb_stop(const struct nlmsghdr *nlh, void *data)
{
return MNL_CB_STOP;
}
static const mnl_cb_t default_cb_array[NLMSG_MIN_TYPE] = {
[NLMSG_NOOP] = mnl_cb_noop,
[NLMSG_ERROR] = mnl_cb_error,
[NLMSG_DONE] = mnl_cb_stop,
[NLMSG_OVERRUN] = mnl_cb_noop,
};
static inline int __mnl_cb_run(const void *buf, size_t numbytes,
unsigned int seq, unsigned int portid,
mnl_cb_t cb_data, void *data,
const mnl_cb_t *cb_ctl_array,
unsigned int cb_ctl_array_len)
{
int ret = MNL_CB_OK, len = numbytes;
const struct nlmsghdr *nlh = buf;
while (mnl_nlmsg_ok(nlh, len)) {
/* check message source */
if (!mnl_nlmsg_portid_ok(nlh, portid)) {
errno = ESRCH;
return -1;
}
/* perform sequence tracking */
if (!mnl_nlmsg_seq_ok(nlh, seq)) {
errno = EPROTO;
return -1;
}
/* dump was interrupted */
if (nlh->nlmsg_flags & NLM_F_DUMP_INTR) {
errno = EINTR;
return -1;
}
/* netlink data message handling */
if (nlh->nlmsg_type >= NLMSG_MIN_TYPE) {
if (cb_data){
ret = cb_data(nlh, data);
if (ret <= MNL_CB_STOP)
goto out;
}
} else if (nlh->nlmsg_type < cb_ctl_array_len) {
if (cb_ctl_array && cb_ctl_array[nlh->nlmsg_type]) {
ret = cb_ctl_array[nlh->nlmsg_type](nlh, data);
if (ret <= MNL_CB_STOP)
goto out;
}
} else if (default_cb_array[nlh->nlmsg_type]) {
ret = default_cb_array[nlh->nlmsg_type](nlh, data);
if (ret <= MNL_CB_STOP)
goto out;
}
nlh = mnl_nlmsg_next(nlh, &len);
}
out:
return ret;
}
/**
* \defgroup callback Callback helpers
* @{
*/
/**
* mnl_cb_run2 - callback runqueue for netlink messages
* \param buf buffer that contains the netlink messages
* \param numbytes number of bytes stored in the buffer
* \param seq sequence number that we expect to receive
* \param portid Netlink PortID that we expect to receive
* \param cb_data callback handler for data messages
* \param data pointer to data that will be passed to the data callback handler
* \param cb_ctl_array array of custom callback handlers from control messages
* \param cb_ctl_array_len array length of custom control callback handlers
*
* You can set the cb_ctl_array to NULL if you want to use the default control
* callback handlers, in that case, the parameter cb_ctl_array_len is not
* checked.
*
* Your callback may return three possible values:
* - MNL_CB_ERROR (<=-1): an error has occurred. Stop callback runqueue.
* - MNL_CB_STOP (=0): stop callback runqueue.
* - MNL_CB_OK (>=1): no problem has occurred.
*
* This function propagates the callback return value. On error, it returns
* -1 and errno is explicitly set. If the portID is not the expected, errno
* is set to ESRCH. If the sequence number is not the expected, errno is set
* to EPROTO. If the dump was interrupted, errno is set to EINTR and you should
* request a new fresh dump again.
*/
int mnl_cb_run2(const void *buf, size_t numbytes,
unsigned int seq, unsigned int portid,
mnl_cb_t cb_data, void *data,
const mnl_cb_t *cb_ctl_array,
unsigned int cb_ctl_array_len)
{
return __mnl_cb_run(buf, numbytes, seq, portid, cb_data, data,
cb_ctl_array, cb_ctl_array_len);
}
/**
* mnl_cb_run - callback runqueue for netlink messages (simplified version)
* \param buf buffer that contains the netlink messages
* \param numbytes number of bytes stored in the buffer
* \param seq sequence number that we expect to receive
* \param portid Netlink PortID that we expect to receive
* \param cb_data callback handler for data messages
* \param data pointer to data that will be passed to the data callback handler
*
* This function is like mnl_cb_run2() but it does not allow you to set
* the control callback handlers.
*
* Your callback may return three possible values:
* - MNL_CB_ERROR (<=-1): an error has occurred. Stop callback runqueue.
* - MNL_CB_STOP (=0): stop callback runqueue.
* - MNL_CB_OK (>=1): no problems has occurred.
*
* This function propagates the callback return value.
*/
int mnl_cb_run(const void *buf, size_t numbytes, unsigned int seq,
unsigned int portid, mnl_cb_t cb_data, void *data)
{
return __mnl_cb_run(buf, numbytes, seq, portid, cb_data, data, NULL, 0);
}
/**
* @}
*/

View File

@ -0,0 +1,5 @@
#ifndef __DHCP_H__
#define __DHCP_H__
int do_dhcp(char *iname);
#endif //__DHCP_H__

View File

@ -0,0 +1,515 @@
/*
* Copyright 2008, The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <dirent.h>
#include <errno.h>
#include <poll.h>
#include <netinet/in.h>
#include <stdarg.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/select.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/types.h>
#include <net/if.h>
#include <time.h>
#include <unistd.h>
#include <net/if.h>
#include "../ifutils.h"
#include "dhcpmsg.h"
#include "packet.h"
#define VERBOSE 2
static int verbose = 1;
static char errmsg[2048];
typedef unsigned long long msecs_t;
#if VERBOSE
void dump_dhcp_msg();
#endif
msecs_t get_msecs(void)
{
struct timespec ts;
if (clock_gettime(CLOCK_MONOTONIC, &ts)) {
return 0;
} else {
return (((msecs_t) ts.tv_sec) * ((msecs_t) 1000)) +
(((msecs_t) ts.tv_nsec) / ((msecs_t) 1000000));
}
}
void printerr(char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
vsnprintf(errmsg, sizeof(errmsg), fmt, ap);
va_end(ap);
printf("%s\n", errmsg);
}
const char *dhcp_lasterror()
{
return errmsg;
}
int fatal(const char *reason)
{
printerr("%s: %s\n", reason, strerror(errno));
return -1;
// exit(1);
}
typedef struct dhcp_info dhcp_info;
struct dhcp_info {
uint32_t type;
uint32_t ipaddr;
uint32_t gateway;
uint32_t prefixLength;
uint32_t dns1;
uint32_t dns2;
uint32_t serveraddr;
uint32_t lease;
};
dhcp_info last_good_info;
void get_dhcp_info(uint32_t *ipaddr, uint32_t *gateway, uint32_t *prefixLength,
uint32_t *dns1, uint32_t *dns2, uint32_t *server,
uint32_t *lease)
{
*ipaddr = last_good_info.ipaddr;
*gateway = last_good_info.gateway;
*prefixLength = last_good_info.prefixLength;
*dns1 = last_good_info.dns1;
*dns2 = last_good_info.dns2;
*server = last_good_info.serveraddr;
*lease = last_good_info.lease;
}
static int dhcp_configure(const char *ifname, dhcp_info *info)
{
last_good_info = *info;
return if_set_network_v4(ifname, info->ipaddr, info->prefixLength, info->gateway,
info->dns1, info->dns2);
}
static const char *dhcp_type_to_name(uint32_t type)
{
switch(type) {
case DHCPDISCOVER: return "discover";
case DHCPOFFER: return "offer";
case DHCPREQUEST: return "request";
case DHCPDECLINE: return "decline";
case DHCPACK: return "ack";
case DHCPNAK: return "nak";
case DHCPRELEASE: return "release";
case DHCPINFORM: return "inform";
default: return "???";
}
}
void dump_dhcp_info(dhcp_info *info)
{
char addr[20], gway[20];
printf("--- dhcp %s (%d) ---\n",
dhcp_type_to_name(info->type), info->type);
strcpy(addr, ipaddr_to_string_v4(info->ipaddr));
strcpy(gway, ipaddr_to_string_v4(info->gateway));
printf("ip %s gw %s prefixLength %d\n", addr, gway, info->prefixLength);
if (info->dns1) printf("dns1: %s\n", ipaddr_to_string_v4(info->dns1));
if (info->dns2) printf("dns2: %s\n", ipaddr_to_string_v4(info->dns2));
printf("server %s, lease %d seconds\n",
ipaddr_to_string_v4(info->serveraddr), info->lease);
}
int decode_dhcp_msg(dhcp_msg *msg, int len, dhcp_info *info)
{
uint8_t *x;
unsigned int opt;
int optlen;
memset(info, 0, sizeof(dhcp_info));
if (len < (DHCP_MSG_FIXED_SIZE + 4)) return -1;
len -= (DHCP_MSG_FIXED_SIZE + 4);
if (msg->options[0] != OPT_COOKIE1) return -1;
if (msg->options[1] != OPT_COOKIE2) return -1;
if (msg->options[2] != OPT_COOKIE3) return -1;
if (msg->options[3] != OPT_COOKIE4) return -1;
x = msg->options + 4;
while (len > 2) {
opt = *x++;
if (opt == OPT_PAD) {
len--;
continue;
}
if (opt == OPT_END) {
break;
}
optlen = *x++;
len -= 2;
if (optlen > len) {
break;
}
switch(opt) {
case OPT_SUBNET_MASK:
if (optlen >= 4) {
in_addr_t mask;
memcpy(&mask, x, 4);
info->prefixLength = mask_to_prefix_v4(mask);
}
break;
case OPT_GATEWAY:
if (optlen >= 4) memcpy(&info->gateway, x, 4);
break;
case OPT_DNS:
if (optlen >= 4) memcpy(&info->dns1, x + 0, 4);
if (optlen >= 8) memcpy(&info->dns2, x + 4, 4);
break;
case OPT_LEASE_TIME:
if (optlen >= 4) {
memcpy(&info->lease, x, 4);
info->lease = ntohl(info->lease);
}
break;
case OPT_SERVER_ID:
if (optlen >= 4) memcpy(&info->serveraddr, x, 4);
break;
case OPT_MESSAGE_TYPE:
info->type = *x;
break;
default:
break;
}
x += optlen;
len -= optlen;
}
info->ipaddr = msg->yiaddr;
return 0;
}
#if VERBOSE
static void hex2str(char *buf, size_t buf_size, const unsigned char *array, int len)
{
int i;
char *cp = buf;
char *buf_end = buf + buf_size;
for (i = 0; i < len; i++) {
cp += snprintf(cp, buf_end - cp, " %02x ", array[i]);
}
}
void dump_dhcp_msg(dhcp_msg *msg, int len)
{
unsigned char *x;
unsigned int n,c;
int optsz;
const char *name;
char buf[2048];
if (len < DHCP_MSG_FIXED_SIZE) {
printf("Invalid length %d, should be %d", len, DHCP_MSG_FIXED_SIZE);
return;
}
len -= DHCP_MSG_FIXED_SIZE;
if (msg->op == OP_BOOTREQUEST)
name = "BOOTREQUEST";
else if (msg->op == OP_BOOTREPLY)
name = "BOOTREPLY";
else
name = "????";
c = msg->hlen > 16 ? 16 : msg->hlen;
hex2str(buf, sizeof(buf), msg->chaddr, c);
for (n = 0; n < 64; n++) {
unsigned char x = msg->sname[n];
if ((x < ' ') || (x > 127)) {
if (x == 0) break;
msg->sname[n] = '.';
}
}
msg->sname[63] = 0;
for (n = 0; n < 128; n++) {
unsigned char x = msg->file[n];
if ((x < ' ') || (x > 127)) {
if (x == 0) break;
msg->file[n] = '.';
}
}
msg->file[127] = 0;
if (len < 4) return;
len -= 4;
x = msg->options + 4;
while (len > 2) {
if (*x == 0) {
x++;
len--;
continue;
}
if (*x == OPT_END) {
break;
}
len -= 2;
optsz = x[1];
if (optsz > len) break;
if (x[0] == OPT_DOMAIN_NAME || x[0] == OPT_MESSAGE) {
if ((unsigned int)optsz < sizeof(buf) - 1) {
n = optsz;
} else {
n = sizeof(buf) - 1;
}
memcpy(buf, &x[2], n);
buf[n] = '\0';
} else {
hex2str(buf, sizeof(buf), &x[2], optsz);
}
if (x[0] == OPT_MESSAGE_TYPE)
name = dhcp_type_to_name(x[2]);
else
name = NULL;
len -= optsz;
x = x + optsz + 2;
}
}
#endif
static int send_message(int sock, int if_index, dhcp_msg *msg, int size)
{
#if VERBOSE > 1
dump_dhcp_msg(msg, size);
#endif
return send_packet(sock, if_index, msg, size, INADDR_ANY, INADDR_BROADCAST,
PORT_BOOTP_CLIENT, PORT_BOOTP_SERVER);
}
static int is_valid_reply(dhcp_msg *msg, dhcp_msg *reply, int sz)
{
if (sz < DHCP_MSG_FIXED_SIZE) {
if (verbose) printf("Wrong size %d != %d\n", sz, DHCP_MSG_FIXED_SIZE);
return 0;
}
if (reply->op != OP_BOOTREPLY) {
if (verbose) printf("Wrong Op %d != %d\n", reply->op, OP_BOOTREPLY);
return 0;
}
if (reply->xid != msg->xid) {
if (verbose) printf("Wrong Xid 0x%x != 0x%x\n", ntohl(reply->xid),
ntohl(msg->xid));
return 0;
}
if (reply->htype != msg->htype) {
if (verbose) printf("Wrong Htype %d != %d\n", reply->htype, msg->htype);
return 0;
}
if (reply->hlen != msg->hlen) {
if (verbose) printf("Wrong Hlen %d != %d\n", reply->hlen, msg->hlen);
return 0;
}
if (memcmp(msg->chaddr, reply->chaddr, msg->hlen)) {
if (verbose) printf("Wrong chaddr %x != %x\n", *(reply->chaddr),*(msg->chaddr));
return 0;
}
return 1;
}
#define STATE_SELECTING 1
#define STATE_REQUESTING 2
#define TIMEOUT_INITIAL 4000
#define TIMEOUT_MAX 32000
int dhcp_init_ifc(const char *ifname)
{
dhcp_msg discover_msg;
dhcp_msg request_msg;
dhcp_msg reply;
dhcp_msg *msg;
dhcp_info info;
int s, r, size;
int valid_reply;
uint32_t xid;
unsigned char hwaddr[6];
struct pollfd pfd;
unsigned int state;
unsigned int timeout;
int if_index;
xid = (uint32_t) get_msecs();
if (if_get_hwaddr(ifname, hwaddr)) {
return fatal("cannot obtain interface address");
}
if ((if_index = if_nametoindex(ifname)) == 0) {
return fatal("cannot obtain interface index");
}
s = open_raw_socket(ifname, hwaddr, if_index);
timeout = TIMEOUT_INITIAL;
state = STATE_SELECTING;
info.type = 0;
goto transmit;
for (;;) {
pfd.fd = s;
pfd.events = POLLIN;
pfd.revents = 0;
r = poll(&pfd, 1, timeout);
if (r == 0) {
#if VERBOSE
printerr("TIMEOUT\n");
#endif
if (timeout >= TIMEOUT_MAX) {
printerr("timed out\n");
if ( info.type == DHCPOFFER ) {
printerr("no acknowledgement from DHCP server\nconfiguring %s with offered parameters\n", ifname);
return dhcp_configure(ifname, &info);
}
errno = ETIME;
close(s);
return -1;
}
timeout = timeout * 2;
transmit:
size = 0;
msg = NULL;
switch(state) {
case STATE_SELECTING:
msg = &discover_msg;
size = init_dhcp_discover_msg(msg, hwaddr, xid);
break;
case STATE_REQUESTING:
msg = &request_msg;
size = init_dhcp_request_msg(msg, hwaddr, xid, info.ipaddr, info.serveraddr);
break;
default:
r = 0;
}
if (size != 0) {
r = send_message(s, if_index, msg, size);
if (r < 0) {
printerr("error sending dhcp msg: %s\n", strerror(errno));
}
}
continue;
}
if (r < 0) {
if ((errno == EAGAIN) || (errno == EINTR)) {
continue;
}
return fatal("poll failed");
}
errno = 0;
r = receive_packet(s, &reply);
if (r < 0) {
if (errno != 0) {
printf("receive_packet failed (%d): %s", r, strerror(errno));
if (errno == ENETDOWN || errno == ENXIO) {
return -1;
}
}
continue;
}
#if VERBOSE > 1
dump_dhcp_msg(&reply, r);
#endif
decode_dhcp_msg(&reply, r, &info);
if (state == STATE_SELECTING) {
valid_reply = is_valid_reply(&discover_msg, &reply, r);
} else {
valid_reply = is_valid_reply(&request_msg, &reply, r);
}
if (!valid_reply) {
printerr("invalid reply\n");
continue;
}
if (verbose) dump_dhcp_info(&info);
switch(state) {
case STATE_SELECTING:
if (info.type == DHCPOFFER) {
state = STATE_REQUESTING;
timeout = TIMEOUT_INITIAL;
xid++;
goto transmit;
}
break;
case STATE_REQUESTING:
if (info.type == DHCPACK) {
printerr("configuring %s\n", ifname);
close(s);
return dhcp_configure(ifname, &info);
} else if (info.type == DHCPNAK) {
printerr("configuration request denied\n");
close(s);
return -1;
} else {
printerr("ignoring %s message in state %d\n",
dhcp_type_to_name(info.type), state);
}
break;
}
}
close(s);
return 0;
}
int do_dhcp(char *iname)
{
if (if_set_addr_v4(iname, 0, 32)) {
printerr("failed to set ip addr for %s to 0.0.0.0: %s\n", iname, strerror(errno));
return -1;
}
if (if_link_up(iname)) {
printerr("failed to bring up interface %s: %s\n", iname, strerror(errno));
return -1;
}
return dhcp_init_ifc(iname);
}

View File

@ -0,0 +1,100 @@
/*
* Copyright 2008, The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <netinet/in.h>
#include "dhcpmsg.h"
static void *init_dhcp_msg(dhcp_msg *msg, int type, void *hwaddr, uint32_t xid)
{
uint8_t *x;
memset(msg, 0, sizeof(dhcp_msg));
msg->op = OP_BOOTREQUEST;
msg->htype = HTYPE_ETHER;
msg->hlen = 6;
msg->hops = 0;
msg->flags = htons(FLAGS_BROADCAST);
msg->xid = xid;
memcpy(msg->chaddr, hwaddr, 6);
x = msg->options;
*x++ = OPT_COOKIE1;
*x++ = OPT_COOKIE2;
*x++ = OPT_COOKIE3;
*x++ = OPT_COOKIE4;
*x++ = OPT_MESSAGE_TYPE;
*x++ = 1;
*x++ = type;
return x;
}
int init_dhcp_discover_msg(dhcp_msg *msg, void *hwaddr, uint32_t xid)
{
uint8_t *x;
x = init_dhcp_msg(msg, DHCPDISCOVER, hwaddr, xid);
*x++ = OPT_PARAMETER_LIST;
*x++ = 4;
*x++ = OPT_SUBNET_MASK;
*x++ = OPT_GATEWAY;
*x++ = OPT_DNS;
*x++ = OPT_BROADCAST_ADDR;
*x++ = OPT_END;
return DHCP_MSG_FIXED_SIZE + (x - msg->options);
}
int init_dhcp_request_msg(dhcp_msg *msg, void *hwaddr, uint32_t xid,
uint32_t ipaddr, uint32_t serveraddr)
{
uint8_t *x;
x = init_dhcp_msg(msg, DHCPREQUEST, hwaddr, xid);
*x++ = OPT_PARAMETER_LIST;
*x++ = 4;
*x++ = OPT_SUBNET_MASK;
*x++ = OPT_GATEWAY;
*x++ = OPT_DNS;
*x++ = OPT_BROADCAST_ADDR;
*x++ = OPT_REQUESTED_IP;
*x++ = 4;
memcpy(x, &ipaddr, 4);
x += 4;
*x++ = OPT_SERVER_ID;
*x++ = 4;
memcpy(x, &serveraddr, 4);
x += 4;
*x++ = OPT_END;
return DHCP_MSG_FIXED_SIZE + (x - msg->options);
}

View File

@ -0,0 +1,106 @@
/*
* Copyright 2008, The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef _WIFI_DHCP_H_
#define _WIFI_DHCP_H_
#include <stdint.h>
#define PORT_BOOTP_SERVER 67
#define PORT_BOOTP_CLIENT 68
/* RFC 2131 p 9 */
typedef struct dhcp_msg dhcp_msg;
#define OP_BOOTREQUEST 1
#define OP_BOOTREPLY 2
#define FLAGS_BROADCAST 0x8000
#define HTYPE_ETHER 1
struct dhcp_msg
{
uint8_t op; /* BOOTREQUEST / BOOTREPLY */
uint8_t htype; /* hw addr type */
uint8_t hlen; /* hw addr len */
uint8_t hops; /* client set to 0 */
uint32_t xid; /* transaction id */
uint16_t secs; /* seconds since start of acq */
uint16_t flags;
uint32_t ciaddr; /* client IP addr */
uint32_t yiaddr; /* your (client) IP addr */
uint32_t siaddr; /* ip addr of next server */
/* (DHCPOFFER and DHCPACK) */
uint32_t giaddr; /* relay agent IP addr */
uint8_t chaddr[16]; /* client hw addr */
char sname[64]; /* asciiz server hostname */
char file[128]; /* asciiz boot file name */
uint8_t options[1024]; /* optional parameters */
};
#define DHCP_MSG_FIXED_SIZE 236
/* first four bytes of options are a cookie to indicate that
** the payload are DHCP options as opposed to some other BOOTP
** extension.
*/
#define OPT_COOKIE1 0x63
#define OPT_COOKIE2 0x82
#define OPT_COOKIE3 0x53
#define OPT_COOKIE4 0x63
/* BOOTP/DHCP options - see RFC 2132 */
#define OPT_PAD 0
#define OPT_SUBNET_MASK 1 /* 4 <ipaddr> */
#define OPT_TIME_OFFSET 2 /* 4 <seconds> */
#define OPT_GATEWAY 3 /* 4*n <ipaddr> * n */
#define OPT_DNS 6 /* 4*n <ipaddr> * n */
#define OPT_DOMAIN_NAME 15 /* n <domainnamestring> */
#define OPT_BROADCAST_ADDR 28 /* 4 <ipaddr> */
#define OPT_REQUESTED_IP 50 /* 4 <ipaddr> */
#define OPT_LEASE_TIME 51 /* 4 <seconds> */
#define OPT_MESSAGE_TYPE 53 /* 1 <msgtype> */
#define OPT_SERVER_ID 54 /* 4 <ipaddr> */
#define OPT_PARAMETER_LIST 55 /* n <optcode> * n */
#define OPT_MESSAGE 56 /* n <errorstring> */
#define OPT_CLASS_ID 60 /* n <opaque> */
#define OPT_CLIENT_ID 61 /* n <opaque> */
#define OPT_END 255
/* DHCP message types */
#define DHCPDISCOVER 1
#define DHCPOFFER 2
#define DHCPREQUEST 3
#define DHCPDECLINE 4
#define DHCPACK 5
#define DHCPNAK 6
#define DHCPRELEASE 7
#define DHCPINFORM 8
int init_dhcp_discover_msg(dhcp_msg *msg, void *hwaddr, uint32_t xid);
int init_dhcp_request_msg(dhcp_msg *msg, void *hwaddr, uint32_t xid,
uint32_t ipaddr, uint32_t serveraddr);
#endif

View File

@ -0,0 +1,247 @@
/*
* Copyright 2008, The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/uio.h>
#include <linux/if_ether.h>
#include <linux/if_packet.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/udp.h>
#include <unistd.h>
#include <stdio.h>
#include "dhcpmsg.h"
int fatal();
int open_raw_socket(const char *ifname __attribute__((unused)), uint8_t *hwaddr, int if_index)
{
int s;
struct sockaddr_ll bindaddr;
if((s = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_IP))) < 0) {
return fatal("socket(PF_PACKET)");
}
memset(&bindaddr, 0, sizeof(bindaddr));
bindaddr.sll_family = AF_PACKET;
bindaddr.sll_protocol = htons(ETH_P_IP);
bindaddr.sll_halen = ETH_ALEN;
memcpy(bindaddr.sll_addr, hwaddr, ETH_ALEN);
bindaddr.sll_ifindex = if_index;
if (bind(s, (struct sockaddr *)&bindaddr, sizeof(bindaddr)) < 0) {
return fatal("Cannot bind raw socket to interface");
}
return s;
}
static uint32_t checksum(void *buffer, unsigned int count, uint32_t startsum)
{
uint16_t *up = (uint16_t *)buffer;
uint32_t sum = startsum;
uint32_t upper16;
while (count > 1) {
sum += *up++;
count -= 2;
}
if (count > 0) {
sum += (uint16_t) *(uint8_t *)up;
}
while ((upper16 = (sum >> 16)) != 0) {
sum = (sum & 0xffff) + upper16;
}
return sum;
}
static uint32_t finish_sum(uint32_t sum)
{
return ~sum & 0xffff;
}
int send_packet(int s, int if_index, struct dhcp_msg *msg, int size,
uint32_t saddr, uint32_t daddr, uint32_t sport, uint32_t dport)
{
struct iphdr ip;
struct udphdr udp;
struct iovec iov[3];
uint32_t udpsum;
uint16_t temp;
struct msghdr msghdr;
struct sockaddr_ll destaddr;
ip.version = IPVERSION;
ip.ihl = sizeof(ip) >> 2;
ip.tos = 0;
ip.tot_len = htons(sizeof(ip) + sizeof(udp) + size);
ip.id = 0;
ip.frag_off = 0;
ip.ttl = IPDEFTTL;
ip.protocol = IPPROTO_UDP;
ip.check = 0;
ip.saddr = saddr;
ip.daddr = daddr;
ip.check = finish_sum(checksum(&ip, sizeof(ip), 0));
udp.source = htons(sport);
udp.dest = htons(dport);
udp.len = htons(sizeof(udp) + size);
udp.check = 0;
/* Calculate checksum for pseudo header */
udpsum = checksum(&ip.saddr, sizeof(ip.saddr), 0);
udpsum = checksum(&ip.daddr, sizeof(ip.daddr), udpsum);
temp = htons(IPPROTO_UDP);
udpsum = checksum(&temp, sizeof(temp), udpsum);
temp = udp.len;
udpsum = checksum(&temp, sizeof(temp), udpsum);
/* Add in the checksum for the udp header */
udpsum = checksum(&udp, sizeof(udp), udpsum);
/* Add in the checksum for the data */
udpsum = checksum(msg, size, udpsum);
udp.check = finish_sum(udpsum);
iov[0].iov_base = (char *)&ip;
iov[0].iov_len = sizeof(ip);
iov[1].iov_base = (char *)&udp;
iov[1].iov_len = sizeof(udp);
iov[2].iov_base = (char *)msg;
iov[2].iov_len = size;
memset(&destaddr, 0, sizeof(destaddr));
destaddr.sll_family = AF_PACKET;
destaddr.sll_protocol = htons(ETH_P_IP);
destaddr.sll_ifindex = if_index;
destaddr.sll_halen = ETH_ALEN;
memcpy(destaddr.sll_addr, "\xff\xff\xff\xff\xff\xff", ETH_ALEN);
msghdr.msg_name = &destaddr;
msghdr.msg_namelen = sizeof(destaddr);
msghdr.msg_iov = iov;
msghdr.msg_iovlen = sizeof(iov) / sizeof(struct iovec);
msghdr.msg_flags = 0;
msghdr.msg_control = 0;
msghdr.msg_controllen = 0;
return sendmsg(s, &msghdr, 0);
}
int receive_packet(int s, struct dhcp_msg *msg)
{
int nread;
int is_valid;
struct dhcp_packet {
struct iphdr ip;
struct udphdr udp;
struct dhcp_msg dhcp;
} packet;
int dhcp_size;
uint32_t sum;
uint16_t temp;
uint32_t saddr, daddr;
nread = read(s, &packet, sizeof(packet));
if (nread < 0) {
return -1;
}
/*
* The raw packet interface gives us all packets received by the
* network interface. We need to filter out all packets that are
* not meant for us.
*/
is_valid = 0;
if (nread < (int)(sizeof(struct iphdr) + sizeof(struct udphdr))) {
#if VERBOSE
ALOGD("Packet is too small (%d) to be a UDP datagram", nread);
#endif
} else if (packet.ip.version != IPVERSION || packet.ip.ihl != (sizeof(packet.ip) >> 2)) {
#if VERBOSE
ALOGD("Not a valid IP packet");
#endif
} else if (nread < ntohs(packet.ip.tot_len)) {
#if VERBOSE
ALOGD("Packet was truncated (read %d, needed %d)", nread, ntohs(packet.ip.tot_len));
#endif
} else if (packet.ip.protocol != IPPROTO_UDP) {
#if VERBOSE
ALOGD("IP protocol (%d) is not UDP", packet.ip.protocol);
#endif
} else if (packet.udp.dest != htons(PORT_BOOTP_CLIENT)) {
#if VERBOSE
ALOGD("UDP dest port (%d) is not DHCP client", ntohs(packet.udp.dest));
#endif
} else {
is_valid = 1;
}
if (!is_valid) {
return -1;
}
/* Seems like it's probably a valid DHCP packet */
/* validate IP header checksum */
sum = finish_sum(checksum(&packet.ip, sizeof(packet.ip), 0));
if (sum != 0) {
printf("IP header checksum failure (0x%x)\n", packet.ip.check);
return -1;
}
/*
* Validate the UDP checksum.
* Since we don't need the IP header anymore, we "borrow" it
* to construct the pseudo header used in the checksum calculation.
*/
dhcp_size = ntohs(packet.udp.len) - sizeof(packet.udp);
/*
* check validity of dhcp_size.
* 1) cannot be negative or zero.
* 2) src buffer contains enough bytes to copy
* 3) cannot exceed destination buffer
*/
if ((dhcp_size <= 0) ||
((int)(nread - sizeof(struct iphdr) - sizeof(struct udphdr)) < dhcp_size) ||
((int)sizeof(struct dhcp_msg) < dhcp_size)) {
#if VERBOSE
printf("Malformed Packet\n");
#endif
return -1;
}
saddr = packet.ip.saddr;
daddr = packet.ip.daddr;
nread = ntohs(packet.ip.tot_len);
memset(&packet.ip, 0, sizeof(packet.ip));
packet.ip.saddr = saddr;
packet.ip.daddr = daddr;
packet.ip.protocol = IPPROTO_UDP;
packet.ip.tot_len = packet.udp.len;
temp = packet.udp.check;
packet.udp.check = 0;
sum = finish_sum(checksum(&packet, nread, 0));
packet.udp.check = temp;
if (!sum)
sum = finish_sum(sum);
if (temp != sum) {
printf("UDP header checksum failure (0x%x should be 0x%x)\n", sum, temp);
return -1;
}
memcpy(msg, &packet.dhcp, dhcp_size);
return dhcp_size;
}

View File

@ -0,0 +1,25 @@
/*
* Copyright 2008, The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef _WIFI_PACKET_H_
#define _WIFI_PACKET_H_
int open_raw_socket(const char *ifname, uint8_t *hwaddr, int if_index);
int send_packet(int s, int if_index, struct dhcp_msg *msg, int size,
uint32_t saddr, uint32_t daddr, uint32_t sport, uint32_t dport);
int receive_packet(int s, struct dhcp_msg *msg);
#endif

View File

@ -0,0 +1,748 @@
/* This example is placed in the public domain. */
#include <netinet/in.h>
#include <arpa/inet.h>
#include <time.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <strings.h>
#include <net/if.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/fcntl.h>
#include <sys/ioctl.h>
#include <linux/if_link.h>
#include <linux/if_ether.h>
#include <linux/rtnetlink.h>
//#include <linux/if.h>
#include "libmnl.h"
#include "ifutils.h"
#define ERRMSG(v...) printf("%s-%d: error=%s %s\n", __func__, __LINE__, strerror(errno), ##v)
extern void dbg_time(const char *fmt, ...);
int mask_to_prefix_v4(uint32_t mask)
{
int ret = 0;
while (mask)
{
mask = mask & (mask - 1);
ret++;
}
return ret;
}
const char *ipaddr_to_string_v4(in_addr_t ipaddr)
{
static char buf[INET6_ADDRSTRLEN] = {'\0'};
buf[0] = '\0';
uint32_t addr = ipaddr;
return inet_ntop(AF_INET, &addr, buf, sizeof(buf));
}
const char *ipaddr_to_string_v6(uint8_t *ipaddr)
{
static char buf[INET6_ADDRSTRLEN] = {'\0'};
buf[0] = '\0';
return inet_ntop(AF_INET6, ipaddr, buf, sizeof(buf));
}
static void ifc_init_ifr(const char *name, struct ifreq *ifr)
{
memset(ifr, 0, sizeof(struct ifreq));
strncpy(ifr->ifr_name, name, IFNAMSIZ);
ifr->ifr_name[IFNAMSIZ - 1] = 0;
}
int if_get_hwaddr(const char *name, void *ptr)
{
int r;
struct ifreq ifr;
ifc_init_ifr(name, &ifr);
int ifc_ctl_sock = socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
if (ifc_ctl_sock < 0)
{
return -1;
}
r = ioctl(ifc_ctl_sock, SIOCGIFHWADDR, &ifr);
if (r < 0)
return -1;
memcpy(ptr, &ifr.ifr_hwaddr.sa_data, ETH_ALEN);
return 0;
}
static int if_act_on_link(const char *ifname, int state)
{
struct mnl_socket *nl;
char buf[MNL_SOCKET_BUFFER_SIZE];
struct nlmsghdr *nlh;
struct ifinfomsg *ifm;
int ret;
unsigned int seq, portid, change = 0, flags = 0;
static int oldstate = -1;
if (state == oldstate)
return 0;
oldstate = state;
if (state)
{
change |= IFF_UP;
flags |= IFF_UP;
}
else
{
change |= IFF_UP;
flags &= ~IFF_UP;
}
nlh = mnl_nlmsg_put_header(buf);
nlh->nlmsg_type = RTM_NEWLINK;
nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
nlh->nlmsg_seq = seq = time(NULL);
ifm = mnl_nlmsg_put_extra_header(nlh, sizeof(*ifm));
ifm->ifi_family = AF_UNSPEC;
ifm->ifi_change = change;
ifm->ifi_flags = flags;
mnl_attr_put_str(nlh, IFLA_IFNAME, ifname);
nl = mnl_socket_open(NETLINK_ROUTE);
if (nl == NULL)
{
ERRMSG("mnl_socket_open");
return -1;
}
if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0)
{
ERRMSG(" mnl_socket_bind");
return -1;
}
portid = mnl_socket_get_portid(nl);
if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0)
{
ERRMSG(" mnl_socket_sendto");
return -1;
}
ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
if (ret == -1)
{
ERRMSG(" mnl_socket_recvfrom");
return -1;
}
ret = mnl_cb_run(buf, ret, seq, portid, NULL, NULL);
if (ret == -1)
{
ERRMSG(" mnl_cb_run");
return -1;
}
mnl_socket_close(nl);
return 0;
}
int if_link_up(const char *ifname)
{
dbg_time("if_link_up %s",ifname);
return if_act_on_link(ifname, 1);
}
int if_link_down(const char *ifname)
{
dbg_time("if_link_down %s",ifname);
return if_act_on_link(ifname, 0);
}
int if_set_mtu(const char *ifname, uint32_t mtu)
{
char buf[MNL_SOCKET_BUFFER_SIZE];
unsigned int seq, portid;
struct mnl_socket *nl;
struct nlmsghdr *nlh;
struct ifinfomsg *ifm;
int ret;
int iface;
static uint32_t oldmtu = 1500;
if (mtu == oldmtu)
return 0;
oldmtu = mtu;
iface = if_nametoindex(ifname);
if (iface == 0)
{
ERRMSG(" if_nametoindex");
return -1;
}
nlh = mnl_nlmsg_put_header(buf);
nlh->nlmsg_type = RTM_NEWLINK;
nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
nlh->nlmsg_seq = seq = time(NULL);
ifm = mnl_nlmsg_put_extra_header(nlh, sizeof(struct ifinfomsg));
ifm->ifi_family = AF_UNSPEC;
ifm->ifi_index = iface;
ifm->ifi_change = 0xFFFFFFFF;
ifm->ifi_type = 0;
ifm->ifi_flags = IFF_NOARP | IFF_MULTICAST;
mnl_attr_put_u32(nlh, IFLA_MTU, mtu);
nl = mnl_socket_open(NETLINK_ROUTE);
if (nl == NULL)
{
ERRMSG(" mnl_socket_open");
return -1;
}
if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0)
{
ERRMSG(" mnl_socket_bind");
return -1;
}
portid = mnl_socket_get_portid(nl);
if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0)
{
ERRMSG(" mnl_socket_sendto");
return -1;
}
ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
if (ret == -1)
{
ERRMSG(" mnl_socket_recvfrom");
return -1;
}
ret = mnl_cb_run(buf, ret, seq, portid, NULL, NULL);
if (ret == -1)
{
ERRMSG(" mnl_cb_run");
return -1;
}
mnl_socket_close(nl);
return 0;
}
/**
* @brief Set the ip addr object
*
* @param operate
* 0 -> add address on interface
* 1 -> delete address on interface
* @param ifname
* @param ipaddr
* @param prefix
* @return int
*/
static int if_act_on_addr(bool operate, int proto, const char *ifname, addr_t *ipaddr, uint32_t prefix)
{
struct mnl_socket *nl;
char buf[MNL_SOCKET_BUFFER_SIZE];
struct nlmsghdr *nlh;
struct ifaddrmsg *ifm;
uint32_t seq, portid;
int ret, family = proto;
int iface;
iface = if_nametoindex(ifname);
if (iface == 0)
{
ERRMSG(" if_nametoindex");
return -1;
}
nlh = mnl_nlmsg_put_header(buf);
if (operate)
nlh->nlmsg_type = RTM_NEWADDR;
else
nlh->nlmsg_type = RTM_DELADDR;
nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_REPLACE | NLM_F_ACK;
nlh->nlmsg_seq = seq = time(NULL);
ifm = mnl_nlmsg_put_extra_header(nlh, sizeof(struct ifaddrmsg));
ifm->ifa_family = family;
ifm->ifa_prefixlen = prefix;
ifm->ifa_flags = IFA_F_PERMANENT;
ifm->ifa_scope = RT_SCOPE_UNIVERSE;
ifm->ifa_index = iface;
/*
* The exact meaning of IFA_LOCAL and IFA_ADDRESS depend
* on the address family being used and the device type.
* For broadcast devices (like the interfaces we use),
* for IPv4 we specify both and they are used interchangeably.
* For IPv6, only IFA_ADDRESS needs to be set.
*/
if (family == AF_INET)
{
mnl_attr_put_u32(nlh, IFA_LOCAL, ipaddr->ip);
mnl_attr_put_u32(nlh, IFA_ADDRESS, ipaddr->ip);
}
else
{
mnl_attr_put(nlh, IFA_ADDRESS, sizeof(struct in6_addr), ipaddr);
}
nl = mnl_socket_open(NETLINK_ROUTE);
if (nl == NULL)
{
ERRMSG(" mnl_socket_open");
return -1;
}
if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0)
{
ERRMSG(" mnl_socket_bind");
return -1;
}
portid = mnl_socket_get_portid(nl);
if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0)
{
ERRMSG(" mnl_socket_sendto");
return -1;
}
ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
if (ret < 0)
{
ERRMSG(" mnl_socket_recvfrom");
return -1;
}
ret = mnl_cb_run(buf, ret, seq, portid, NULL, NULL);
if (ret < 0)
{
ERRMSG(" mnl_cb_run");
return -1;
}
mnl_socket_close(nl);
return 0;
}
int if_set_addr_v4(const char *ifname, in_addr_t ipaddr, uint32_t prefix)
{
addr_t addr;
addr.ip = ipaddr;
return if_act_on_addr(1, AF_INET, ifname, &addr, prefix);
}
int if_del_addr_v4(const char *ifname, in_addr_t ipaddr, uint32_t prefix)
{
addr_t addr;
addr.ip = ipaddr;
return if_act_on_addr(0, AF_INET, ifname, &addr, prefix);
}
int if_set_addr_v6(const char *ifname, uint8_t *ipaddr, uint32_t prefix)
{
addr_t addr;
memcpy(&addr.ip6, ipaddr, 16);
return if_act_on_addr(1, AF_INET6, ifname, &addr, prefix);
}
int if_del_addr_v6(const char *ifname, uint8_t *ipaddr, uint32_t prefix)
{
addr_t addr;
memcpy(&addr.ip6, ipaddr, 16);
return if_act_on_addr(0, AF_INET6, ifname, &addr, prefix);
}
static int data_attr_cb(const struct nlattr *attr, void *data)
{
const struct nlattr **tb = data;
int type = mnl_attr_get_type(attr);
/* skip unsupported attribute in user-space */
if (mnl_attr_type_valid(attr, IFA_MAX) < 0)
return MNL_CB_OK;
switch (type)
{
case IFA_ADDRESS:
if (mnl_attr_validate(attr, MNL_TYPE_BINARY) < 0)
{
ERRMSG(" mnl_attr_validate");
return MNL_CB_ERROR;
}
break;
}
tb[type] = attr;
return MNL_CB_OK;
}
static int data_cb(const struct nlmsghdr *nlh, void *data)
{
struct nlattr *tb[IFA_MAX + 1] = {};
struct ifaddrmsg *ifa = mnl_nlmsg_get_payload(nlh);
struct addrinfo_t *addrinfo = (struct addrinfo_t *)data;
void *addr = NULL;
mnl_attr_parse(nlh, sizeof(*ifa), data_attr_cb, tb);
if (tb[IFA_ADDRESS])
{
char out[INET6_ADDRSTRLEN];
addr = mnl_attr_get_payload(tb[IFLA_ADDRESS]);
addr = mnl_attr_get_payload(tb[IFA_ADDRESS]);
if (!inet_ntop(ifa->ifa_family, addr, out, sizeof(out)))
ERRMSG("inet_ntop");
// printf("%d %d-> %d %s\n", addrinfo->iface, ifa->ifa_index, ifa->ifa_scope, out);
addrinfo->addrs[addrinfo->num].prefix = ifa->ifa_prefixlen;
if (ifa->ifa_index == addrinfo->iface)
{
if (ifa->ifa_family == AF_INET6)
memcpy(addrinfo->addrs[addrinfo->num].address.ip6.s6_addr, addr, 16);
if (ifa->ifa_family == AF_INET)
memcpy(&(addrinfo->addrs[addrinfo->num].address.ip), addr, 4);
addrinfo->num++;
}
}
// ifa->ifa_scope
// 0: global
// 200: site
// 253: link
// 254: host
// 255: nowhere
return MNL_CB_OK;
}
/**
* @brief
*
* @param ifname
* @param proto
* AF_INET -> for IPv4
* AF_INET6 -> for IPv6
* @return int
*/
static int if_get_addr(const char *ifname, int proto, struct addrinfo_t *addrinfo)
{
char buf[MNL_SOCKET_BUFFER_SIZE];
unsigned int seq, portid;
struct mnl_socket *nl;
struct nlmsghdr *nlh;
struct rtgenmsg *rt;
int ret;
addrinfo->iface = if_nametoindex(ifname);
if (addrinfo->iface == 0)
{
ERRMSG(" if_nametoindex");
return -1;
}
nlh = mnl_nlmsg_put_header(buf);
nlh->nlmsg_type = RTM_GETADDR;
nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
nlh->nlmsg_seq = seq = time(NULL);
rt = mnl_nlmsg_put_extra_header(nlh, sizeof(struct rtgenmsg));
if (proto == AF_INET)
rt->rtgen_family = AF_INET;
else if (proto == AF_INET6)
rt->rtgen_family = AF_INET6;
nl = mnl_socket_open(NETLINK_ROUTE);
if (nl == NULL)
{
ERRMSG(" mnl_socket_open");
return -1;
}
if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0)
{
ERRMSG(" mnl_socket_bind");
return -1;
}
portid = mnl_socket_get_portid(nl);
if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0)
{
ERRMSG(" mnl_socket_sendto");
return -1;
}
ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
while (ret > 0)
{
ret = mnl_cb_run(buf, ret, seq, portid, data_cb, addrinfo);
if (ret <= MNL_CB_STOP)
break;
ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
}
if (ret == -1)
{
ERRMSG(" error");
return -1;
}
mnl_socket_close(nl);
return 0;
}
int if_flush_v4_addr(const char *ifname)
{
struct addrinfo_t addrinfo;
int i = 0;
memset(&addrinfo, 0, sizeof(struct addrinfo_t));
if_get_addr(ifname, AF_INET, &addrinfo);
for (; i < addrinfo.num; i++)
{
// printf("remove address: %s\n", ipaddr_to_string_v4(addrinfo.addrs[i].address.ip));
if_del_addr_v4(ifname, addrinfo.addrs[i].address.ip, addrinfo.addrs[i].prefix);
}
return 0;
}
int if_flush_v6_addr(const char *ifname)
{
struct addrinfo_t addrinfo;
int i = 0;
memset(&addrinfo, 0, sizeof(struct addrinfo_t));
if_get_addr(ifname, AF_INET6, &addrinfo);
for (; i < addrinfo.num; i++)
{
// printf("remove address: %s\n", ipaddr_to_string_v6(addrinfo.addrs[i].address.ip6.s6_addr));
if_del_addr_v6(ifname, addrinfo.addrs[i].address.ip6.s6_addr, addrinfo.addrs[i].prefix);
}
return 0;
}
/**
* @brief Set the route addr object
* Usage:
* iface destination cidr [gateway]
* Example:
* eth0 10.0.1.12 32 10.0.1.11
* eth0 ffff::10.0.1.12 128 fdff::1
* @param operate
* add or del
* @param ifname
* @param dstaddr
* @param prefix
* @param gwaddr
* @return int
*/
int if_act_on_route(bool operate, int proto, const char *ifname, addr_t *dstaddr, uint32_t prefix, addr_t *gwaddr)
{
struct mnl_socket *nl;
char buf[MNL_SOCKET_BUFFER_SIZE];
struct nlmsghdr *nlh;
struct rtmsg *rtm;
uint32_t seq, portid;
int iface, ret, family = proto;
iface = if_nametoindex(ifname);
if (iface == 0)
{
ERRMSG(" if_nametoindex");
return -1;
}
nlh = mnl_nlmsg_put_header(buf);
if (operate)
nlh->nlmsg_type = RTM_NEWROUTE;
else
nlh->nlmsg_type = RTM_DELROUTE;
nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_ACK;
nlh->nlmsg_seq = seq = time(NULL);
rtm = mnl_nlmsg_put_extra_header(nlh, sizeof(struct rtmsg));
rtm->rtm_family = family;
rtm->rtm_dst_len = prefix;
rtm->rtm_src_len = 0;
rtm->rtm_tos = 0;
rtm->rtm_protocol = RTPROT_STATIC;
rtm->rtm_table = RT_TABLE_MAIN;
rtm->rtm_type = RTN_UNICAST;
/* is there any gateway? */
rtm->rtm_scope = gwaddr ? RT_SCOPE_UNIVERSE : RT_SCOPE_LINK;
rtm->rtm_flags = 0;
if (family == AF_INET)
mnl_attr_put_u32(nlh, RTA_DST, dstaddr->ip);
else
mnl_attr_put(nlh, RTA_DST, sizeof(struct in6_addr), dstaddr);
mnl_attr_put_u32(nlh, RTA_OIF, iface);
if (gwaddr)
{
if (family == AF_INET)
mnl_attr_put_u32(nlh, RTA_GATEWAY, gwaddr->ip);
else
{
mnl_attr_put(nlh, RTA_GATEWAY, sizeof(struct in6_addr), gwaddr);
}
}
nl = mnl_socket_open(NETLINK_ROUTE);
if (nl == NULL)
{
ERRMSG(" mnl_socket_open");
return -1;
}
if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0)
{
ERRMSG(" mnl_socket_bind");
return -1;
}
portid = mnl_socket_get_portid(nl);
if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0)
{
ERRMSG(" mnl_socket_sendto");
return -1;
}
ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
if (ret < 0)
{
ERRMSG(" mnl_socket_recvfrom");
return -1;
}
ret = mnl_cb_run(buf, ret, seq, portid, NULL, NULL);
if (ret < 0)
{
ERRMSG(" mnl_cb_run");
return -1;
}
mnl_socket_close(nl);
return 0;
}
int if_set_default_route_v4(const char *ifname)
{
return if_act_on_route(1, AF_INET, ifname, (addr_t *)&in6addr_any, 0, NULL);
}
int if_del_default_route_v4(const char *ifname)
{
return if_act_on_route(0, AF_INET, ifname, (addr_t *)&in6addr_any, 0, NULL);
}
int if_set_default_route_v6(const char *ifname)
{
return if_act_on_route(1, AF_INET6, ifname, (addr_t *)&in6addr_any, 0, NULL);
}
int if_del_default_route_v6(const char *ifname)
{
return if_act_on_route(0, AF_INET6, ifname, (addr_t *)&in6addr_any, 0, NULL);
}
/**
* @brief Set the default gwaddr object
* set default gw
* @param operate
* @param ifname
* @param gwaddr
* gateway ip
* @return int
*/
int if_set_route_gw_v4(const char *ifname, in_addr_t gwaddr)
{
addr_t addr;
memset(&addr, 0, sizeof(addr_t));
addr.ip = gwaddr;
return if_act_on_route(1, AF_INET, ifname, (addr_t *)&in6addr_any, 0, &addr);
}
int if_del_route_gw_v4(const char *ifname, in_addr_t gwaddr)
{
addr_t addr;
memset(&addr, 0, sizeof(addr_t));
addr.ip = gwaddr;
return if_act_on_route(0, AF_INET, ifname, (addr_t *)&in6addr_any, 0, &addr);
}
int if_set_route_gw_v6(const char *ifname, uint8_t *gwaddr)
{
addr_t addr;
memset(&addr, 0, sizeof(addr_t));
memcpy(&addr.ip6, gwaddr, 16);
return if_act_on_route(1, AF_INET6, ifname, (addr_t *)&in6addr_any, 0, &addr);
}
int if_del_route_gw_v6(const char *ifname, uint8_t *gwaddr)
{
addr_t addr;
memset(&addr, 0, sizeof(addr_t));
memcpy(&addr.ip6, gwaddr, 16);
return if_act_on_route(0, AF_INET6, ifname, (addr_t *)&in6addr_any, 0, &addr);
}
int if_set_dns(const char *dns1, const char *dns2)
{
int ret = 0;
char buf[128] = {'\0'};
int fd = open("/etc/resolv.conf", O_CREAT | O_WRONLY | O_TRUNC);
if (fd < 0)
{
ERRMSG(" fail to open /etc/resolv.conf");
return -1;
}
if (dns1)
snprintf(buf, sizeof(buf), "nameserver %s\n", dns1);
if (dns2)
snprintf(buf, sizeof(buf), "nameserver %s\n", dns2);
ret = write(fd, buf, strlen(buf));
if (ret < 0)
{
ERRMSG(" write dns");
}
close(fd);
return ret > 0 ? 0 : -1;
}
int if_set_network_v4(const char *ifname, in_addr_t ipaddr, uint32_t prefix,
in_addr_t gwaddr, in_addr_t dns1, in_addr_t dns2)
{
if_link_up(ifname);
sleep(2);
if_set_addr_v4(ifname, ipaddr, prefix);
if_set_default_route_v4(ifname);
if_set_dns(ipaddr_to_string_v4(dns1), ipaddr_to_string_v4(dns2));
return 0;
}
int if_set_network_v6(const char *ifname, uint8_t *ipaddr, uint32_t prefix,
uint8_t *gwaddr, uint8_t *dns1, uint8_t *dns2)
{
if_link_up(ifname);
sleep(2);
if_set_addr_v6(ifname, ipaddr, prefix);
if_set_default_route_v6(ifname);
if_set_dns(ipaddr_to_string_v6(dns1), ipaddr_to_string_v6(dns2));
return 0;
}

View File

@ -0,0 +1,53 @@
#ifndef __IFUTILS_H__
#define __IFUTILS_H__
typedef union {
in_addr_t ip;
struct in6_addr ip6;
} addr_t;
#define MAX_IP_NUM 32
struct addrinfo_t
{
int iface;
int num;
struct
{
int prefix;
addr_t address;
} addrs[MAX_IP_NUM];
};
const char *ipaddr_to_string_v4(in_addr_t ipaddr);
const char *ipaddr_to_string_v6(uint8_t *ipaddr);
int mask_to_prefix_v4(in_addr_t mask);
int if_get_hwaddr(const char *name, void *ptr);
int if_link_down(const char *ifname);
int if_link_up(const char *ifname);
int if_set_mtu(const char *ifname, uint32_t mtu);
int if_set_addr_v4(const char *name, in_addr_t address, uint32_t prefixlen);
int if_del_addr_v4(const char *name, in_addr_t address, uint32_t prefixlen);
int if_set_addr_v6(const char *name, uint8_t *address, uint32_t prefixlen);
int if_del_addr_v6(const char *name, uint8_t *address, uint32_t prefixlen);
int if_flush_v4_addr(const char *ifname);
int if_flush_v6_addr(const char *ifname);
int if_set_route_gw_v4(const char *ifname, in_addr_t gwaddr);
int if_del_route_gw_v4(const char *ifname, in_addr_t gwaddr);
int if_set_default_route_v4(const char *ifname);
int if_del_default_route_v4(const char *ifname);
int if_set_route_gw_v6(const char *ifname, uint8_t *gwaddr);
int if_del_route_gw_v6(const char *ifname, uint8_t *gwaddr);
int if_set_default_route_v6(const char *ifname);
int if_del_default_route_v6(const char *ifname);
int if_set_network_v4(const char *ifname, in_addr_t ipaddr, uint32_t prefix,
in_addr_t gwaddr, in_addr_t dns1, in_addr_t dns2);
int if_set_network_v6(const char *ifname, uint8_t *ipaddr, uint32_t prefix,
uint8_t *gwaddr, uint8_t *dns1, uint8_t *dns2);
#endif //__IFUTILS_H__

View File

@ -0,0 +1,202 @@
#ifndef _LIBMNL_H_
#define _LIBMNL_H_
#include <stdbool.h>
#include <stdio.h>
#include <stdint.h>
#include <unistd.h>
#include <sys/socket.h> /* for sa_family_t */
#include <linux/netlink.h>
#ifdef __cplusplus
extern "C" {
#endif
/*
* Netlink socket API
*/
#define MNL_SOCKET_AUTOPID 0
#define MNL_SOCKET_BUFFER_SIZE (sysconf(_SC_PAGESIZE) < 8192L ? sysconf(_SC_PAGESIZE) : 8192L)
#define MNL_SOCKET_DUMP_SIZE 32768
struct mnl_socket;
extern struct mnl_socket *mnl_socket_open(int bus);
extern struct mnl_socket *mnl_socket_open2(int bus, int flags);
extern struct mnl_socket *mnl_socket_fdopen(int fd);
extern int mnl_socket_bind(struct mnl_socket *nl, unsigned int groups, pid_t pid);
extern int mnl_socket_close(struct mnl_socket *nl);
extern int mnl_socket_get_fd(const struct mnl_socket *nl);
extern unsigned int mnl_socket_get_portid(const struct mnl_socket *nl);
extern ssize_t mnl_socket_sendto(const struct mnl_socket *nl, const void *req, size_t siz);
extern ssize_t mnl_socket_recvfrom(const struct mnl_socket *nl, void *buf, size_t siz);
extern int mnl_socket_setsockopt(const struct mnl_socket *nl, int type, void *buf, socklen_t len);
extern int mnl_socket_getsockopt(const struct mnl_socket *nl, int type, void *buf, socklen_t *len);
/*
* Netlink message API
*/
#define MNL_ALIGNTO 4
#define MNL_ALIGN(len) (((len)+MNL_ALIGNTO-1) & ~(MNL_ALIGNTO-1))
#define MNL_NLMSG_HDRLEN MNL_ALIGN(sizeof(struct nlmsghdr))
extern size_t mnl_nlmsg_size(size_t len);
extern size_t mnl_nlmsg_get_payload_len(const struct nlmsghdr *nlh);
/* Netlink message header builder */
extern struct nlmsghdr *mnl_nlmsg_put_header(void *buf);
extern void *mnl_nlmsg_put_extra_header(struct nlmsghdr *nlh, size_t size);
/* Netlink message iterators */
extern bool mnl_nlmsg_ok(const struct nlmsghdr *nlh, int len);
extern struct nlmsghdr *mnl_nlmsg_next(const struct nlmsghdr *nlh, int *len);
/* Netlink sequence tracking */
extern bool mnl_nlmsg_seq_ok(const struct nlmsghdr *nlh, unsigned int seq);
/* Netlink portID checking */
extern bool mnl_nlmsg_portid_ok(const struct nlmsghdr *nlh, unsigned int portid);
/* Netlink message getters */
extern void *mnl_nlmsg_get_payload(const struct nlmsghdr *nlh);
extern void *mnl_nlmsg_get_payload_offset(const struct nlmsghdr *nlh, size_t offset);
extern void *mnl_nlmsg_get_payload_tail(const struct nlmsghdr *nlh);
/* Netlink message printer */
extern void mnl_nlmsg_fprintf(FILE *fd, const void *data, size_t datalen, size_t extra_header_size);
/* Message batch helpers */
struct mnl_nlmsg_batch;
extern struct mnl_nlmsg_batch *mnl_nlmsg_batch_start(void *buf, size_t bufsiz);
extern bool mnl_nlmsg_batch_next(struct mnl_nlmsg_batch *b);
extern void mnl_nlmsg_batch_stop(struct mnl_nlmsg_batch *b);
extern size_t mnl_nlmsg_batch_size(struct mnl_nlmsg_batch *b);
extern void mnl_nlmsg_batch_reset(struct mnl_nlmsg_batch *b);
extern void *mnl_nlmsg_batch_head(struct mnl_nlmsg_batch *b);
extern void *mnl_nlmsg_batch_current(struct mnl_nlmsg_batch *b);
extern bool mnl_nlmsg_batch_is_empty(struct mnl_nlmsg_batch *b);
/*
* Netlink attributes API
*/
#define MNL_ATTR_HDRLEN MNL_ALIGN(sizeof(struct nlattr))
/* TLV attribute getters */
extern uint16_t mnl_attr_get_type(const struct nlattr *attr);
extern uint16_t mnl_attr_get_len(const struct nlattr *attr);
extern uint16_t mnl_attr_get_payload_len(const struct nlattr *attr);
extern void *mnl_attr_get_payload(const struct nlattr *attr);
extern uint8_t mnl_attr_get_u8(const struct nlattr *attr);
extern uint16_t mnl_attr_get_u16(const struct nlattr *attr);
extern uint32_t mnl_attr_get_u32(const struct nlattr *attr);
extern uint64_t mnl_attr_get_u64(const struct nlattr *attr);
extern const char *mnl_attr_get_str(const struct nlattr *attr);
/* TLV attribute putters */
extern void mnl_attr_put(struct nlmsghdr *nlh, uint16_t type, size_t len, const void *data);
extern void mnl_attr_put_u8(struct nlmsghdr *nlh, uint16_t type, uint8_t data);
extern void mnl_attr_put_u16(struct nlmsghdr *nlh, uint16_t type, uint16_t data);
extern void mnl_attr_put_u32(struct nlmsghdr *nlh, uint16_t type, uint32_t data);
extern void mnl_attr_put_u64(struct nlmsghdr *nlh, uint16_t type, uint64_t data);
extern void mnl_attr_put_str(struct nlmsghdr *nlh, uint16_t type, const char *data);
extern void mnl_attr_put_strz(struct nlmsghdr *nlh, uint16_t type, const char *data);
/* TLV attribute putters with buffer boundary checkings */
extern bool mnl_attr_put_check(struct nlmsghdr *nlh, size_t buflen, uint16_t type, size_t len, const void *data);
extern bool mnl_attr_put_u8_check(struct nlmsghdr *nlh, size_t buflen, uint16_t type, uint8_t data);
extern bool mnl_attr_put_u16_check(struct nlmsghdr *nlh, size_t buflen, uint16_t type, uint16_t data);
extern bool mnl_attr_put_u32_check(struct nlmsghdr *nlh, size_t buflen, uint16_t type, uint32_t data);
extern bool mnl_attr_put_u64_check(struct nlmsghdr *nlh, size_t buflen, uint16_t type, uint64_t data);
extern bool mnl_attr_put_str_check(struct nlmsghdr *nlh, size_t buflen, uint16_t type, const char *data);
extern bool mnl_attr_put_strz_check(struct nlmsghdr *nlh, size_t buflen, uint16_t type, const char *data);
/* TLV attribute nesting */
extern struct nlattr *mnl_attr_nest_start(struct nlmsghdr *nlh, uint16_t type);
extern struct nlattr *mnl_attr_nest_start_check(struct nlmsghdr *nlh, size_t buflen, uint16_t type);
extern void mnl_attr_nest_end(struct nlmsghdr *nlh, struct nlattr *start);
extern void mnl_attr_nest_cancel(struct nlmsghdr *nlh, struct nlattr *start);
/* TLV validation */
extern int mnl_attr_type_valid(const struct nlattr *attr, uint16_t maxtype);
enum mnl_attr_data_type {
MNL_TYPE_UNSPEC,
MNL_TYPE_U8,
MNL_TYPE_U16,
MNL_TYPE_U32,
MNL_TYPE_U64,
MNL_TYPE_STRING,
MNL_TYPE_FLAG,
MNL_TYPE_MSECS,
MNL_TYPE_NESTED,
MNL_TYPE_NESTED_COMPAT,
MNL_TYPE_NUL_STRING,
MNL_TYPE_BINARY,
MNL_TYPE_MAX,
};
extern int mnl_attr_validate(const struct nlattr *attr, enum mnl_attr_data_type type);
extern int mnl_attr_validate2(const struct nlattr *attr, enum mnl_attr_data_type type, size_t len);
/* TLV iterators */
extern bool mnl_attr_ok(const struct nlattr *attr, int len);
extern struct nlattr *mnl_attr_next(const struct nlattr *attr);
#define mnl_attr_for_each(attr, nlh, offset) \
for ((attr) = mnl_nlmsg_get_payload_offset((nlh), (offset)); \
mnl_attr_ok((attr), (char *)mnl_nlmsg_get_payload_tail(nlh) - (char *)(attr)); \
(attr) = mnl_attr_next(attr))
#define mnl_attr_for_each_nested(attr, nest) \
for ((attr) = mnl_attr_get_payload(nest); \
mnl_attr_ok((attr), (char *)mnl_attr_get_payload(nest) + mnl_attr_get_payload_len(nest) - (char *)(attr)); \
(attr) = mnl_attr_next(attr))
#define mnl_attr_for_each_payload(payload, payload_size) \
for ((attr) = (payload); \
mnl_attr_ok((attr), (char *)(payload) + payload_size - (char *)(attr)); \
(attr) = mnl_attr_next(attr))
/* TLV callback-based attribute parsers */
typedef int (*mnl_attr_cb_t)(const struct nlattr *attr, void *data);
extern int mnl_attr_parse(const struct nlmsghdr *nlh, unsigned int offset, mnl_attr_cb_t cb, void *data);
extern int mnl_attr_parse_nested(const struct nlattr *attr, mnl_attr_cb_t cb, void *data);
extern int mnl_attr_parse_payload(const void *payload, size_t payload_len, mnl_attr_cb_t cb, void *data);
/*
* callback API
*/
#define MNL_CB_ERROR -1
#define MNL_CB_STOP 0
#define MNL_CB_OK 1
typedef int (*mnl_cb_t)(const struct nlmsghdr *nlh, void *data);
extern int mnl_cb_run(const void *buf, size_t numbytes, unsigned int seq,
unsigned int portid, mnl_cb_t cb_data, void *data);
extern int mnl_cb_run2(const void *buf, size_t numbytes, unsigned int seq,
unsigned int portid, mnl_cb_t cb_data, void *data,
const mnl_cb_t *cb_ctl_array,
unsigned int cb_ctl_array_len);
/*
* other declarations
*/
#ifndef SOL_NETLINK
#define SOL_NETLINK 270
#endif
#ifndef MNL_ARRAY_SIZE
#define MNL_ARRAY_SIZE(a) (sizeof(a)/sizeof((a)[0]))
#endif
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif

View File

@ -0,0 +1,556 @@
/*
* (C) 2008-2010 by Pablo Neira Ayuso <pablo@netfilter.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*/
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <errno.h>
#include <string.h>
#include "libmnl.h"
/**
* \defgroup nlmsg Netlink message helpers
*
* Netlink message:
* \verbatim
|<----------------- 4 bytes ------------------->|
|<----- 2 bytes ------>|<------- 2 bytes ------>|
|-----------------------------------------------|
| Message length (including header) |
|-----------------------------------------------|
| Message type | Message flags |
|-----------------------------------------------|
| Message sequence number |
|-----------------------------------------------|
| Netlink PortID |
|-----------------------------------------------|
| |
. Payload .
|_______________________________________________|
\endverbatim
*
* There is usually an extra header after the the Netlink header (at the
* beginning of the payload). This extra header is specific of the Netlink
* subsystem. After this extra header, it comes the sequence of attributes
* that are expressed in Type-Length-Value (TLV) format.
*
* @{
*/
/**
* mnl_nlmsg_size - calculate the size of Netlink message (without alignment)
* \param len length of the Netlink payload
*
* This function returns the size of a netlink message (header plus payload)
* without alignment.
*/
size_t mnl_nlmsg_size(size_t len)
{
return len + MNL_NLMSG_HDRLEN;
}
/**
* mnl_nlmsg_get_payload_len - get the length of the Netlink payload
* \param nlh pointer to the header of the Netlink message
*
* This function returns the Length of the netlink payload, ie. the length
* of the full message minus the size of the Netlink header.
*/
size_t mnl_nlmsg_get_payload_len(const struct nlmsghdr *nlh)
{
return nlh->nlmsg_len - MNL_NLMSG_HDRLEN;
}
/**
* mnl_nlmsg_put_header - reserve and prepare room for Netlink header
* \param buf memory already allocated to store the Netlink header
*
* This function sets to zero the room that is required to put the Netlink
* header in the memory buffer passed as parameter. This function also
* initializes the nlmsg_len field to the size of the Netlink header. This
* function returns a pointer to the Netlink header structure.
*/
struct nlmsghdr *mnl_nlmsg_put_header(void *buf)
{
int len = MNL_ALIGN(sizeof(struct nlmsghdr));
struct nlmsghdr *nlh = buf;
memset(buf, 0, len);
nlh->nlmsg_len = len;
return nlh;
}
/**
* mnl_nlmsg_put_extra_header - reserve and prepare room for an extra header
* \param nlh pointer to Netlink header
* \param size size of the extra header that we want to put
*
* This function sets to zero the room that is required to put the extra
* header after the initial Netlink header. This function also increases
* the nlmsg_len field. You have to invoke mnl_nlmsg_put_header() before
* you call this function. This function returns a pointer to the extra
* header.
*/
void *mnl_nlmsg_put_extra_header(struct nlmsghdr *nlh,
size_t size)
{
char *ptr = (char *)nlh + nlh->nlmsg_len;
size_t len = MNL_ALIGN(size);
nlh->nlmsg_len += len;
memset(ptr, 0, len);
return ptr;
}
/**
* mnl_nlmsg_get_payload - get a pointer to the payload of the netlink message
* \param nlh pointer to a netlink header
*
* This function returns a pointer to the payload of the netlink message.
*/
void *mnl_nlmsg_get_payload(const struct nlmsghdr *nlh)
{
return (void *)nlh + MNL_NLMSG_HDRLEN;
}
/**
* mnl_nlmsg_get_payload_offset - get a pointer to the payload of the message
* \param nlh pointer to a netlink header
* \param offset offset to the payload of the attributes TLV set
*
* This function returns a pointer to the payload of the netlink message plus
* a given offset.
*/
void *mnl_nlmsg_get_payload_offset(const struct nlmsghdr *nlh,
size_t offset)
{
return (void *)nlh + MNL_NLMSG_HDRLEN + MNL_ALIGN(offset);
}
/**
* mnl_nlmsg_ok - check a there is room for netlink message
* \param nlh netlink message that we want to check
* \param len remaining bytes in a buffer that contains the netlink message
*
* This function is used to check that a buffer that contains a netlink
* message has enough room for the netlink message that it stores, ie. this
* function can be used to verify that a netlink message is not malformed nor
* truncated.
*
* This function does not set errno in case of error since it is intended
* for iterations. Thus, it returns true on success and false on error.
*
* The len parameter may become negative in malformed messages during message
* iteration, that is why we use a signed integer.
*/
bool mnl_nlmsg_ok(const struct nlmsghdr *nlh, int len)
{
return len >= (int)sizeof(struct nlmsghdr) &&
nlh->nlmsg_len >= sizeof(struct nlmsghdr) &&
(int)nlh->nlmsg_len <= len;
}
/**
* mnl_nlmsg_next - get the next netlink message in a multipart message
* \param nlh current netlink message that we are handling
* \param len length of the remaining bytes in the buffer (passed by reference).
*
* This function returns a pointer to the next netlink message that is part
* of a multi-part netlink message. Netlink can batch several messages into
* one buffer so that the receiver has to iterate over the whole set of
* Netlink messages.
*
* You have to use mnl_nlmsg_ok() to check if the next Netlink message is
* valid.
*/
struct nlmsghdr *mnl_nlmsg_next(const struct nlmsghdr *nlh,
int *len)
{
*len -= MNL_ALIGN(nlh->nlmsg_len);
return (struct nlmsghdr *)((void *)nlh + MNL_ALIGN(nlh->nlmsg_len));
}
/**
* mnl_nlmsg_get_payload_tail - get the ending of the netlink message
* \param nlh pointer to netlink message
*
* This function returns a pointer to the netlink message tail. This is useful
* to build a message since we continue adding attributes at the end of the
* message.
*/
void *mnl_nlmsg_get_payload_tail(const struct nlmsghdr *nlh)
{
return (void *)nlh + MNL_ALIGN(nlh->nlmsg_len);
}
/**
* mnl_nlmsg_seq_ok - perform sequence tracking
* \param nlh current netlink message that we are handling
* \param seq last sequence number used to send a message
*
* This functions returns true if the sequence tracking is fulfilled, otherwise
* false is returned. We skip the tracking for netlink messages whose sequence
* number is zero since it is usually reserved for event-based kernel
* notifications. On the other hand, if seq is set but the message sequence
* number is not set (i.e. this is an event message coming from kernel-space),
* then we also skip the tracking. This approach is good if we use the same
* socket to send commands to kernel-space (that we want to track) and to
* listen to events (that we do not track).
*/
bool mnl_nlmsg_seq_ok(const struct nlmsghdr *nlh,
unsigned int seq)
{
return nlh->nlmsg_seq && seq ? nlh->nlmsg_seq == seq : true;
}
/**
* mnl_nlmsg_portid_ok - perform portID origin check
* \param nlh current netlink message that we are handling
* \param portid netlink portid that we want to check
*
* This functions returns true if the origin is fulfilled, otherwise
* false is returned. We skip the tracking for netlink message whose portID
* is zero since it is reserved for event-based kernel notifications. On the
* other hand, if portid is set but the message PortID is not (i.e. this
* is an event message coming from kernel-space), then we also skip the
* tracking. This approach is good if we use the same socket to send commands
* to kernel-space (that we want to track) and to listen to events (that we
* do not track).
*/
bool mnl_nlmsg_portid_ok(const struct nlmsghdr *nlh,
unsigned int portid)
{
return nlh->nlmsg_pid && portid ? nlh->nlmsg_pid == portid : true;
}
static void mnl_nlmsg_fprintf_header(FILE *fd, const struct nlmsghdr *nlh)
{
fprintf(fd, "----------------\t------------------\n");
fprintf(fd, "| %.010u |\t| message length |\n", nlh->nlmsg_len);
fprintf(fd, "| %.05u | %c%c%c%c |\t| type | flags |\n",
nlh->nlmsg_type,
nlh->nlmsg_flags & NLM_F_REQUEST ? 'R' : '-',
nlh->nlmsg_flags & NLM_F_MULTI ? 'M' : '-',
nlh->nlmsg_flags & NLM_F_ACK ? 'A' : '-',
nlh->nlmsg_flags & NLM_F_ECHO ? 'E' : '-');
fprintf(fd, "| %.010u |\t| sequence number|\n", nlh->nlmsg_seq);
fprintf(fd, "| %.010u |\t| port ID |\n", nlh->nlmsg_pid);
fprintf(fd, "----------------\t------------------\n");
}
static void mnl_nlmsg_fprintf_payload(FILE *fd, const struct nlmsghdr *nlh,
size_t extra_header_size)
{
int rem = 0;
unsigned int i;
for (i=sizeof(struct nlmsghdr); i<nlh->nlmsg_len; i+=4) {
char *b = (char *) nlh;
struct nlattr *attr = (struct nlattr *) (b+i);
/* netlink control message. */
if (nlh->nlmsg_type < NLMSG_MIN_TYPE) {
fprintf(fd, "| %.2x %.2x %.2x %.2x |\t",
0xff & b[i], 0xff & b[i+1],
0xff & b[i+2], 0xff & b[i+3]);
fprintf(fd, "| |\n");
/* special handling for the extra header. */
} else if (extra_header_size > 0) {
extra_header_size -= 4;
fprintf(fd, "| %.2x %.2x %.2x %.2x |\t",
0xff & b[i], 0xff & b[i+1],
0xff & b[i+2], 0xff & b[i+3]);
fprintf(fd, "| extra header |\n");
/* this seems like an attribute header. */
} else if (rem == 0 && (attr->nla_type & NLA_TYPE_MASK) != 0) {
fprintf(fd, "|%c[%d;%dm"
"%.5u"
"%c[%dm"
"|"
"%c[%d;%dm"
"%c%c"
"%c[%dm"
"|"
"%c[%d;%dm"
"%.5u"
"%c[%dm|\t",
27, 1, 31,
attr->nla_len,
27, 0,
27, 1, 32,
attr->nla_type & NLA_F_NESTED ? 'N' : '-',
attr->nla_type &
NLA_F_NET_BYTEORDER ? 'B' : '-',
27, 0,
27, 1, 34,
attr->nla_type & NLA_TYPE_MASK,
27, 0);
fprintf(fd, "|len |flags| type|\n");
if (!(attr->nla_type & NLA_F_NESTED)) {
rem = NLA_ALIGN(attr->nla_len) -
sizeof(struct nlattr);
}
/* this is the attribute payload. */
} else if (rem > 0) {
rem -= 4;
fprintf(fd, "| %.2x %.2x %.2x %.2x |\t",
0xff & b[i], 0xff & b[i+1],
0xff & b[i+2], 0xff & b[i+3]);
fprintf(fd, "| data |");
fprintf(fd, "\t %c %c %c %c\n",
isprint(b[i]) ? b[i] : ' ',
isprint(b[i+1]) ? b[i+1] : ' ',
isprint(b[i+2]) ? b[i+2] : ' ',
isprint(b[i+3]) ? b[i+3] : ' ');
}
}
fprintf(fd, "----------------\t------------------\n");
}
/**
* mnl_nlmsg_fprintf - print netlink message to file
* \param fd pointer to file type
* \param data pointer to the buffer that contains messages to be printed
* \param datalen length of data stored in the buffer
* \param extra_header_size size of the extra header (if any)
*
* This function prints the netlink header to a file handle.
* It may be useful for debugging purposes. One example of the output
* is the following:
*
*\verbatim
---------------- ------------------
| 0000000040 | | message length |
| 00016 | R-A- | | type | flags |
| 1289148991 | | sequence number|
| 0000000000 | | port ID |
---------------- ------------------
| 00 00 00 00 | | extra header |
| 00 00 00 00 | | extra header |
| 01 00 00 00 | | extra header |
| 01 00 00 00 | | extra header |
|00008|--|00003| |len |flags| type|
| 65 74 68 30 | | data | e t h 0
---------------- ------------------
\endverbatim
*
* This example above shows the netlink message that is send to kernel-space
* to set up the link interface eth0. The netlink and attribute header data
* are displayed in base 10 whereas the extra header and the attribute payload
* are expressed in base 16. The possible flags in the netlink header are:
*
* - R, that indicates that NLM_F_REQUEST is set.
* - M, that indicates that NLM_F_MULTI is set.
* - A, that indicates that NLM_F_ACK is set.
* - E, that indicates that NLM_F_ECHO is set.
*
* The lack of one flag is displayed with '-'. On the other hand, the possible
* attribute flags available are:
*
* - N, that indicates that NLA_F_NESTED is set.
* - B, that indicates that NLA_F_NET_BYTEORDER is set.
*/
void mnl_nlmsg_fprintf(FILE *fd, const void *data, size_t datalen,
size_t extra_header_size)
{
const struct nlmsghdr *nlh = data;
int len = datalen;
while (mnl_nlmsg_ok(nlh, len)) {
mnl_nlmsg_fprintf_header(fd, nlh);
mnl_nlmsg_fprintf_payload(fd, nlh, extra_header_size);
nlh = mnl_nlmsg_next(nlh, &len);
}
}
/**
* @}
*/
/**
* \defgroup batch Netlink message batch helpers
*
* This library provides helpers to batch several messages into one single
* datagram. These helpers do not perform strict memory boundary checkings.
*
* The following figure represents a Netlink message batch:
*
* |<-------------- MNL_SOCKET_BUFFER_SIZE ------------->|
* |<-------------------- batch ------------------>| |
* |-----------|-----------|-----------|-----------|-----------|
* |<- nlmsg ->|<- nlmsg ->|<- nlmsg ->|<- nlmsg ->|<- nlmsg ->|
* |-----------|-----------|-----------|-----------|-----------|
* ^ ^
* | |
* message N message N+1
*
* To start the batch, you have to call mnl_nlmsg_batch_start() and you can
* use mnl_nlmsg_batch_stop() to release it.
*
* You have to invoke mnl_nlmsg_batch_next() to get room for a new message
* in the batch. If this function returns NULL, it means that the last
* message that was added (message N+1 in the figure above) does not fit the
* batch. Thus, you have to send the batch (which includes until message N)
* and, then, you have to call mnl_nlmsg_batch_reset() to re-initialize
* the batch (this moves message N+1 to the head of the buffer). For that
* reason, the buffer that you have to use to store the batch must be double
* of MNL_SOCKET_BUFFER_SIZE to ensure that the last message (message N+1)
* that did not fit into the batch is written inside valid memory boundaries.
*
* @{
*/
struct mnl_nlmsg_batch {
/* the buffer that is used to store the batch. */
void *buf;
size_t limit;
size_t buflen;
/* the current netlink message in the batch. */
void *cur;
bool overflow;
};
/**
* mnl_nlmsg_batch_start - initialize a batch
* \param buf pointer to the buffer that will store this batch
* \param limit maximum size of the batch (should be MNL_SOCKET_BUFFER_SIZE).
*
* The buffer that you pass must be double of MNL_SOCKET_BUFFER_SIZE. The
* limit must be half of the buffer size, otherwise expect funny memory
* corruptions 8-).
*
* You can allocate the buffer that you use to store the batch in the stack or
* the heap, no restrictions in this regard. This function returns NULL on
* error.
*/
struct mnl_nlmsg_batch *mnl_nlmsg_batch_start(void *buf,
size_t limit)
{
struct mnl_nlmsg_batch *b;
b = malloc(sizeof(struct mnl_nlmsg_batch));
if (b == NULL)
return NULL;
b->buf = buf;
b->limit = limit;
b->buflen = 0;
b->cur = buf;
b->overflow = false;
return b;
}
/**
* mnl_nlmsg_batch_stop - release a batch
* \param b pointer to batch
*
* This function releases the batch allocated by mnl_nlmsg_batch_start().
*/
void mnl_nlmsg_batch_stop(struct mnl_nlmsg_batch *b)
{
free(b);
}
/**
* mnl_nlmsg_batch_next - get room for the next message in the batch
* \param b pointer to batch
*
* This function returns false if the last message did not fit into the
* batch. Otherwise, it prepares the batch to provide room for the new
* Netlink message in the batch and returns true.
*
* You have to put at least one message in the batch before calling this
* function, otherwise your application is likely to crash.
*/
bool mnl_nlmsg_batch_next(struct mnl_nlmsg_batch *b)
{
struct nlmsghdr *nlh = b->cur;
if (b->buflen + nlh->nlmsg_len > b->limit) {
b->overflow = true;
return false;
}
b->cur = b->buf + b->buflen + nlh->nlmsg_len;
b->buflen += nlh->nlmsg_len;
return true;
}
/**
* mnl_nlmsg_batch_reset - reset the batch
* \param b pointer to batch
*
* This function allows to reset a batch, so you can reuse it to create a
* new one. This function moves the last message which does not fit the
* batch to the head of the buffer, if any.
*/
void mnl_nlmsg_batch_reset(struct mnl_nlmsg_batch *b)
{
if (b->overflow) {
struct nlmsghdr *nlh = b->cur;
memcpy(b->buf, b->cur, nlh->nlmsg_len);
b->buflen = nlh->nlmsg_len;
b->cur = b->buf + b->buflen;
b->overflow = false;
} else {
b->buflen = 0;
b->cur = b->buf;
}
}
/**
* mnl_nlmsg_batch_size - get current size of the batch
* \param b pointer to batch
*
* This function returns the current size of the batch.
*/
size_t mnl_nlmsg_batch_size(struct mnl_nlmsg_batch *b)
{
return b->buflen;
}
/**
* mnl_nlmsg_batch_head - get head of this batch
* \param b pointer to batch
*
* This function returns a pointer to the head of the batch, which is the
* beginning of the buffer that is used.
*/
void *mnl_nlmsg_batch_head(struct mnl_nlmsg_batch *b)
{
return b->buf;
}
/**
* mnl_nlmsg_batch_current - returns current position in the batch
* \param b pointer to batch
*
* This function returns a pointer to the current position in the buffer
* that is used to store the batch.
*/
void *mnl_nlmsg_batch_current(struct mnl_nlmsg_batch *b)
{
return b->cur;
}
/**
* mnl_nlmsg_batch_is_empty - check if there is any message in the batch
* \param b pointer to batch
*
* This function returns true if the batch is empty.
*/
bool mnl_nlmsg_batch_is_empty(struct mnl_nlmsg_batch *b)
{
return b->buflen == 0;
}
/**
* @}
*/

View File

@ -0,0 +1,351 @@
/*
* (C) 2008-2010 by Pablo Neira Ayuso <pablo@netfilter.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*/
#include <sys/types.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <unistd.h>
#include <time.h>
#include <errno.h>
#include "libmnl.h"
/**
* \mainpage
*
* libmnl is a minimalistic user-space library oriented to Netlink developers.
* There are a lot of common tasks in parsing, validating, constructing of
* both the Netlink header and TLVs that are repetitive and easy to get wrong.
* This library aims to provide simple helpers that allows you to avoid
* re-inventing the wheel in common Netlink tasks.
*
* \verbatim
"Simplify, simplify" -- Henry David Thoureau. Walden (1854)
\endverbatim
*
* The acronym libmnl stands for LIBrary Minimalistic NetLink.
*
* libmnl homepage is:
* http://www.netfilter.org/projects/libmnl/
*
* \section features Main Features
* - Small: the shared library requires around 30KB for an x86-based computer.
* - Simple: this library avoids complex abstractions that tend to hide Netlink
* details. It avoids elaborated object-oriented infrastructure and complex
* callback-based workflow.
* - Easy to use: the library simplifies the work for Netlink-wise developers.
* It provides functions to make socket handling, message building,
* validating, parsing and sequence tracking, easier.
* - Easy to re-use: you can use this library to build your own abstraction
* layer upon this library, if you want to provide another library that
* hides Netlink details to your users.
* - Decoupling: the interdependency of the main bricks that compose this
* library is reduced, i.e. the library provides many helpers, but the
* programmer is not forced to use them.
*
* \section licensing Licensing terms
* This library is released under the LGPLv2.1 or any later (at your option).
*
* \section Dependencies
* You have to install the Linux kernel headers that you want to use to develop
* your application. Moreover, this library requires that you have some basics
* on Netlink.
*
* \section scm Git Tree
* The current development version of libmnl can be accessed at:
* http://git.netfilter.org/cgi-bin/gitweb.cgi?p=libmnl.git;a=summary
*
* \section using Using libmnl
* You can access several example files under examples/ in the libmnl source
* code tree.
*/
struct mnl_socket {
int fd;
struct sockaddr_nl addr;
};
/**
* \defgroup socket Netlink socket helpers
* @{
*/
/**
* mnl_socket_get_fd - obtain file descriptor from netlink socket
* \param nl netlink socket obtained via mnl_socket_open()
*
* This function returns the file descriptor of a given netlink socket.
*/
int mnl_socket_get_fd(const struct mnl_socket *nl)
{
return nl->fd;
}
/**
* mnl_socket_get_portid - obtain Netlink PortID from netlink socket
* \param nl netlink socket obtained via mnl_socket_open()
*
* This function returns the Netlink PortID of a given netlink socket.
* It's a common mistake to assume that this PortID equals the process ID
* which is not always true. This is the case if you open more than one
* socket that is binded to the same Netlink subsystem from the same process.
*/
unsigned int mnl_socket_get_portid(const struct mnl_socket *nl)
{
return nl->addr.nl_pid;
}
static struct mnl_socket *__mnl_socket_open(int bus, int flags)
{
struct mnl_socket *nl;
nl = calloc(1, sizeof(struct mnl_socket));
if (nl == NULL)
return NULL;
nl->fd = socket(AF_NETLINK, SOCK_RAW | flags, bus);
if (nl->fd == -1) {
free(nl);
return NULL;
}
return nl;
}
/**
* mnl_socket_open - open a netlink socket
* \param bus the netlink socket bus ID (see NETLINK_* constants)
*
* On error, it returns NULL and errno is appropriately set. Otherwise, it
* returns a valid pointer to the mnl_socket structure.
*/
struct mnl_socket *mnl_socket_open(int bus)
{
return __mnl_socket_open(bus, 0);
}
/**
* mnl_socket_open2 - open a netlink socket with appropriate flags
* \param bus the netlink socket bus ID (see NETLINK_* constants)
* \param flags the netlink socket flags (see SOCK_* constants in socket(2))
*
* This is similar to mnl_socket_open(), but allows to set flags like
* SOCK_CLOEXEC at socket creation time (useful for multi-threaded programs
* performing exec calls).
*
* On error, it returns NULL and errno is appropriately set. Otherwise, it
* returns a valid pointer to the mnl_socket structure.
*/
struct mnl_socket *mnl_socket_open2(int bus, int flags)
{
return __mnl_socket_open(bus, flags);
}
/**
* mnl_socket_fdopen - associates a mnl_socket object with pre-existing socket.
* \param fd pre-existing socket descriptor.
*
* On error, it returns NULL and errno is appropriately set. Otherwise, it
* returns a valid pointer to the mnl_socket structure. It also sets the portID
* if the socket fd is already bound and it is AF_NETLINK.
*
* Note that mnl_socket_get_portid() returns 0 if this function is used with
* non-netlink socket.
*/
struct mnl_socket *mnl_socket_fdopen(int fd)
{
int ret;
struct mnl_socket *nl;
struct sockaddr_nl addr;
socklen_t addr_len = sizeof(struct sockaddr_nl);
ret = getsockname(fd, (struct sockaddr *) &addr, &addr_len);
if (ret == -1)
return NULL;
nl = calloc(1, sizeof(struct mnl_socket));
if (nl == NULL)
return NULL;
nl->fd = fd;
if (addr.nl_family == AF_NETLINK)
nl->addr = addr;
return nl;
}
/**
* mnl_socket_bind - bind netlink socket
* \param nl netlink socket obtained via mnl_socket_open()
* \param groups the group of message you're interested in
* \param pid the port ID you want to use (use zero for automatic selection)
*
* On error, this function returns -1 and errno is appropriately set. On
* success, 0 is returned. You can use MNL_SOCKET_AUTOPID which is 0 for
* automatic port ID selection.
*/
int mnl_socket_bind(struct mnl_socket *nl, unsigned int groups,
pid_t pid)
{
int ret;
socklen_t addr_len;
nl->addr.nl_family = AF_NETLINK;
nl->addr.nl_groups = groups;
nl->addr.nl_pid = pid;
ret = bind(nl->fd, (struct sockaddr *) &nl->addr, sizeof (nl->addr));
if (ret < 0)
return ret;
addr_len = sizeof(nl->addr);
ret = getsockname(nl->fd, (struct sockaddr *) &nl->addr, &addr_len);
if (ret < 0)
return ret;
if (addr_len != sizeof(nl->addr)) {
errno = EINVAL;
return -1;
}
if (nl->addr.nl_family != AF_NETLINK) {
errno = EINVAL;
return -1;
}
return 0;
}
/**
* mnl_socket_sendto - send a netlink message of a certain size
* \param nl netlink socket obtained via mnl_socket_open()
* \param buf buffer containing the netlink message to be sent
* \param len number of bytes in the buffer that you want to send
*
* On error, it returns -1 and errno is appropriately set. Otherwise, it
* returns the number of bytes sent.
*/
ssize_t mnl_socket_sendto(const struct mnl_socket *nl,
const void *buf, size_t len)
{
static const struct sockaddr_nl snl = {
.nl_family = AF_NETLINK
};
return sendto(nl->fd, buf, len, 0,
(struct sockaddr *) &snl, sizeof(snl));
}
/**
* mnl_socket_recvfrom - receive a netlink message
* \param nl netlink socket obtained via mnl_socket_open()
* \param buf buffer that you want to use to store the netlink message
* \param bufsiz size of the buffer passed to store the netlink message
*
* On error, it returns -1 and errno is appropriately set. If errno is set
* to ENOSPC, it means that the buffer that you have passed to store the
* netlink message is too small, so you have received a truncated message.
* To avoid this, you have to allocate a buffer of MNL_SOCKET_BUFFER_SIZE
* (which is 8KB, see linux/netlink.h for more information). Using this
* buffer size ensures that your buffer is big enough to store the netlink
* message without truncating it.
*/
ssize_t mnl_socket_recvfrom(const struct mnl_socket *nl,
void *buf, size_t bufsiz)
{
ssize_t ret;
struct sockaddr_nl addr;
struct iovec iov = {
.iov_base = buf,
.iov_len = bufsiz,
};
struct msghdr msg = {
.msg_name = &addr,
.msg_namelen = sizeof(struct sockaddr_nl),
.msg_iov = &iov,
.msg_iovlen = 1,
.msg_control = NULL,
.msg_controllen = 0,
.msg_flags = 0,
};
ret = recvmsg(nl->fd, &msg, 0);
if (ret == -1)
return ret;
if (msg.msg_flags & MSG_TRUNC) {
errno = ENOSPC;
return -1;
}
if (msg.msg_namelen != sizeof(struct sockaddr_nl)) {
errno = EINVAL;
return -1;
}
return ret;
}
/**
* mnl_socket_close - close a given netlink socket
* \param nl netlink socket obtained via mnl_socket_open()
*
* On error, this function returns -1 and errno is appropriately set.
* On success, it returns 0.
*/
int mnl_socket_close(struct mnl_socket *nl)
{
int ret = close(nl->fd);
free(nl);
return ret;
}
/**
* mnl_socket_setsockopt - set Netlink socket option
* \param nl netlink socket obtained via mnl_socket_open()
* \param type type of Netlink socket options
* \param buf the buffer that contains the data about this option
* \param len the size of the buffer passed
*
* This function allows you to set some Netlink socket option. As of writing
* this (see linux/netlink.h), the existing options are:
*
* - \#define NETLINK_ADD_MEMBERSHIP 1
* - \#define NETLINK_DROP_MEMBERSHIP 2
* - \#define NETLINK_PKTINFO 3
* - \#define NETLINK_BROADCAST_ERROR 4
* - \#define NETLINK_NO_ENOBUFS 5
*
* In the early days, Netlink only supported 32 groups expressed in a
* 32-bits mask. However, since 2.6.14, Netlink may have up to 2^32 multicast
* groups but you have to use setsockopt() with NETLINK_ADD_MEMBERSHIP to
* join a given multicast group. This function internally calls setsockopt()
* to join a given netlink multicast group. You can still use mnl_bind()
* and the 32-bit mask to join a set of Netlink multicast groups.
*
* On error, this function returns -1 and errno is appropriately set.
*/
int mnl_socket_setsockopt(const struct mnl_socket *nl, int type,
void *buf, socklen_t len)
{
return setsockopt(nl->fd, SOL_NETLINK, type, buf, len);
}
/**
* mnl_socket_getsockopt - get a Netlink socket option
* \param nl netlink socket obtained via mnl_socket_open()
* \param type type of Netlink socket options
* \param buf pointer to the buffer to store the value of this option
* \param len size of the information written in the buffer
*
* On error, this function returns -1 and errno is appropriately set.
*/
int mnl_socket_getsockopt(const struct mnl_socket *nl, int type,
void *buf, socklen_t *len)
{
return getsockopt(nl->fd, SOL_NETLINK, type, buf, len);
}
/**
* @}
*/

1770
fibocom-dial/src/main.c Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,67 @@
[Setting]
qmap_num=4
[profile1]
apn=1234567890
user=qwertyuio
password=asdfghjkl
auth=2
ipfamily=1
pin=
[profile2]
apn=ctnet
user=
password=
auth=0
ipfamily=1
pin=
[profile3]
apn=ctnet
user=
password=
auth=0
ipfamily=1
pin=
[profile4]
apn=ctnet
user=
password=
auth=0
ipfamily=1
pin=
[profile5]
apn=ctnet
user=
password=
auth=
ipfamily=1
pin=
[profile6]
apn=ctnet
user=
password=
auth=
ipfamily=1
pin=
[profile7]
apn=ctnet
user=
password=
auth=
ipfamily=1
pin=
[profile8]
apn=ctnet
user=
password=
auth=
ipfamily=1
pin=

View File

@ -0,0 +1,438 @@
#include "QMIThread.h"
//2021-03-15 zhangkaibo@fibocom.com changed begin for oa 20210311037
#include <net/if.h>
//2021-03-15 zhangkaibo@fibocom.com changed end for oa 20210311037
static size_t fibo_fread(const char *filename, void *buf, size_t size)
{
FILE *fp = fopen(filename, "r");
size_t n = 0;
memset(buf, 0x00, size);
if (fp) {
n = fread(buf, 1, size, fp);
if (n <= 0 || n == size) {
dbg_time(
"warnning: fail to fread(%s), fread=%zd, buf_size=%zd, "
"errno: %d (%s)",
__func__, filename, n, size, errno, strerror(errno));
}
fclose(fp);
}
return n > 0 ? n : 0;
}
static size_t fibo_fwrite(const char *filename, const void *buf, size_t size)
{
FILE *fp = fopen(filename, "w");
size_t n = 0;
if (fp) {
n = fwrite(buf, 1, size, fp);
if (n != size) {
dbg_time(
"warnning: fail to fwrite(%s), fwrite=%zd, buf_size=%zd, "
"errno: %d (%s)",
__func__, filename, n, size, errno, strerror(errno));
}
fclose(fp);
}
return n > 0 ? n : 0;
}
static int fibo_iface_is_in_bridge(const char *iface)
{
char filename[256];
snprintf(filename, sizeof(filename), "/sys/class/net/%s/brport", iface);
return (access(filename, F_OK) == 0 || errno != ENOENT);
}
int fibo_bridge_mode_detect(PROFILE_T *profile)
{
const char *ifname = profile->qmapnet_adapter ? profile->qmapnet_adapter
: profile->usbnet_adapter;
const char *driver;
char bridge_mode[128];
char bridge_ipv4[128];
char ipv4[128];
char buf[64];
size_t n;
int in_bridge;
driver = profile->driver_name;
snprintf(bridge_mode, sizeof(bridge_mode), "/sys/class/net/%s/bridge_mode",
ifname);
snprintf(bridge_ipv4, sizeof(bridge_ipv4), "/sys/class/net/%s/bridge_ipv4",
ifname);
if (access(bridge_mode, F_OK) && errno == ENOENT) {
snprintf(bridge_mode, sizeof(bridge_mode),
"/sys/module/%s/parameters/bridge_mode", driver);
snprintf(bridge_ipv4, sizeof(bridge_ipv4),
"/sys/module/%s/parameters/bridge_ipv4", driver);
if (access(bridge_mode, F_OK) && errno == ENOENT) {
bridge_mode[0] = '\0';
}
}
in_bridge = fibo_iface_is_in_bridge(ifname);
if (in_bridge) {
dbg_time("notice: iface %s had add to bridge\n", ifname);
}
if (in_bridge && bridge_mode[0] == '\0') {
dbg_time("warnning: can not find bride_mode file for %s\n", ifname);
return 1;
}
n = fibo_fread(bridge_mode, buf, sizeof(buf));
if (in_bridge) {
if (n <= 0 || buf[0] == '0') {
dbg_time("warnning: should set 1 to bride_mode file for %s\n",
ifname);
return 1;
}
} else {
if (n <= 0 || buf[0] == '0') {
return 0;
}
}
memset(ipv4, 0, sizeof(ipv4));
if (strstr(bridge_ipv4, "/sys/class/net/") || profile->qmap_mode == 0 ||
profile->qmap_mode == 1) {
snprintf(ipv4, sizeof(ipv4), "0x%x", profile->ipv4.Address);
dbg_time("echo '%s' > %s", ipv4, bridge_ipv4);
fibo_fwrite(bridge_ipv4, ipv4, strlen(ipv4));
} else {
snprintf(ipv4, sizeof(ipv4), "0x%x:%d", profile->ipv4.Address,
profile->muxid);
dbg_time("echo '%s' > %s", ipv4, bridge_ipv4);
fibo_fwrite(bridge_ipv4, ipv4, strlen(ipv4));
}
return 1;
}
//2021-03-15 zhangkaibo@fibocom.com changed begin for oa 20210311037
void fibo_get_driver_info(PROFILE_T *profile, RMNET_INFO *rmnet_info) {
int ifc_ctl_sock;
struct ifreq ifr;
int rc;
int request = 0x89F3;
unsigned char data[512];
memset(rmnet_info, 0x00, sizeof(*rmnet_info));
ifc_ctl_sock = socket(AF_INET, SOCK_DGRAM, 0);
if (ifc_ctl_sock <= 0) {
dbg_time("socket() failed: %s\n", strerror(errno));
return;
}
memset(&ifr, 0, sizeof(struct ifreq));
strncpy(ifr.ifr_name, profile->usbnet_adapter, IFNAMSIZ);
ifr.ifr_name[IFNAMSIZ - 1] = 0;
ifr.ifr_ifru.ifru_data = (void *)data;
rc = ioctl(ifc_ctl_sock, request, &ifr);
if (rc < 0) {
dbg_time("ioctl(0x%x, qmap_settings) failed: %s, rc=%d", request, strerror(errno), rc);
}
else {
memcpy(rmnet_info, data, sizeof(*rmnet_info));
}
close(ifc_ctl_sock);
}
//2021-03-15 zhangkaibo@fibocom.com changed end for oa 20210311037
int fibo_qmap_mode_detect(PROFILE_T *profile)
{
int n;
char buf[128];
char qmap_netcard[128];
struct {
char filename[255 * 2];
char linkname[255 * 2];
} * pl;
pl = (typeof(pl))malloc(sizeof(*pl));
snprintf(pl->linkname, sizeof(pl->linkname),
"/sys/class/net/%s/device/driver", profile->usbnet_adapter);
n = readlink(pl->linkname, pl->filename, sizeof(pl->filename));
pl->filename[n] = '\0';
while (pl->filename[n] != '/') n--;
profile->driver_name = strdup(&pl->filename[n + 1]);
//2021-03-15 zhangkaibo@fibocom.com changed begin for oa 20210311037
fibo_get_driver_info(profile, &profile->rmnet_info);
if (profile->rmnet_info.size) {
profile->qmap_mode = profile->rmnet_info.qmap_mode;
if (profile->qmap_mode) {
int offset_id = profile->pdp - 1;
if (profile->qmap_mode == 1)
offset_id = 0;
profile->muxid = profile->rmnet_info.mux_id[offset_id];
profile->qmapnet_adapter = strdup( profile->rmnet_info.ifname[offset_id]);
profile->qmap_size = profile->rmnet_info.rx_urb_size;
profile->qmap_version = profile->rmnet_info.qmap_version;
}
goto _out;
}
//2021-03-15 zhangkaibo@fibocom.com changed end for oa 20210311037
if (qmidev_is_pciemhi(profile->qmichannel)) {
profile->qmap_mode = 1;
if (profile->muxid == 0 || profile->muxid == 0x81)
{
profile->muxid = 0x81;
}
else
{
if (profile->muxid < 0x80)
profile->muxid += 0x81;
profile->qmap_mode = 2;
}
profile->qmapnet_adapter = strdup(profile->usbnet_adapter);
goto _final_process;
}
snprintf(pl->filename, sizeof(pl->filename), "/sys/class/net/%s/qmap_num",
profile->usbnet_adapter);
//2021-01-27 willa.liu@fibocom.com changed begin for support mantis 0068849
if (access(pl->filename, F_OK) == 0) {
dbg_time("access %s", pl->filename);
if (errno != ENOENT) {
dbg_time("fail to access %s, errno: %d (%s)", pl->filename, errno,
strerror(errno));
goto _out;
}
//2021-02-01 willa.liu@fibocom.com changed begin for support mantis 0069837
/*
snprintf(pl->filename, sizeof(pl->filename),
"/sys/module/%s/parameters/qmap_num", profile->driver_name);
if (access(pl->filename, R_OK)) {
if (errno != ENOENT) {
dbg_time("fail to access %s, errno: %d (%s)", pl->filename,
errno, strerror(errno));
goto _out;
}
snprintf(
pl->filename, sizeof(pl->filename),
"/sys/class/net/%s/device/driver/module/parameters/qmap_num",
profile->usbnet_adapter);
if (access(pl->filename, R_OK)) {
if (errno != ENOENT) {
dbg_time("fail to access %s, errno: %d (%s)", pl->filename,
errno, strerror(errno));
goto _out;
}
}
}
*/
//2021-02-01 willa.liu@fibocom.com changed end for support mantis 0069837
}
else
{
snprintf(pl->filename, sizeof(pl->filename), "/sys/class/net/%s/qmap_mode",
profile->usbnet_adapter);
if (access(pl->filename, F_OK) == 0) {
dbg_time("access %s", pl->filename);
if (errno != ENOENT) {
dbg_time("fail to access %s, errno: %d (%s)", pl->filename, errno,
strerror(errno));
goto _out;
}
}
}
if(!access(pl->filename, R_OK))
{
n = fibo_fread(pl->filename, buf, sizeof(buf));
if(n > 0)
{
profile->qmap_mode = atoi(buf);
if(profile->qmap_mode >= 1 && qmidev_is_pciemhi(profile->qmichannel))
{
profile->muxid =
profile->pdp + 0x80;
sprintf(qmap_netcard, "%s.%d", profile->usbnet_adapter,profile->pdp);
profile->qmapnet_adapter = strdup(qmap_netcard);
}
if(qmidev_is_gobinet(profile->qmichannel) || qmidev_is_qmiwwan(profile->qmichannel))
{
if(profile->qmap_mode > 1)
{
profile->muxid =
profile->pdp + 0x80;
sprintf(qmap_netcard, "%s.%d", profile->usbnet_adapter,profile->pdp);
profile->qmapnet_adapter = strdup(qmap_netcard);
}
if(profile->qmap_mode == 1)
{
profile->muxid = 0x81;
profile->qmapnet_adapter = strdup(profile->usbnet_adapter);
}
}
}
if (0) {
profile->qmap_mode = atoi(buf);
if (profile->qmap_mode > 1 && qmidev_is_gobinet(profile->qmichannel)) {
profile->muxid =
profile->pdp + 0x80; // muxis is 0x8X for PDN-X
sprintf(qmap_netcard, "%s.%d", profile->usbnet_adapter,profile->pdp);
profile->qmapnet_adapter = strdup(qmap_netcard);
}
if (profile->qmap_mode >= 1 && !qmidev_is_gobinet(profile->qmichannel)) {
profile->muxid =
profile->pdp + 0x80; // muxis is 0x8X for PDN-X
sprintf(qmap_netcard, "%s.%d", profile->usbnet_adapter,profile->pdp);
profile->qmapnet_adapter = strdup(qmap_netcard);
}
if (profile->qmap_mode == 1 && qmidev_is_gobinet(profile->qmichannel)) {
profile->muxid = 0x81;
profile->qmapnet_adapter = strdup(profile->usbnet_adapter);
//2021-01-27 willa.liu@fibocom.com changed end for support mantis 0068849
}
}
} else if (qmidev_is_qmiwwan(profile->qmichannel)) {
snprintf(pl->filename, sizeof(pl->filename), "/sys/class/net/qmimux%d",
profile->pdp - 1);
if (access(pl->filename, R_OK)) {
if (errno != ENOENT) {
dbg_time("fail to access %s, errno: %d (%s)", pl->filename,
errno, strerror(errno));
}
goto _out;
}
// upstream Kernel Style QMAP qmi_wwan.c
snprintf(pl->filename, sizeof(pl->filename),
"/sys/class/net/%s/qmi/add_mux", profile->usbnet_adapter);
n = fibo_fread(pl->filename, buf, sizeof(buf));
if (n >= 5) {
profile->qmap_mode = n / 5; // 0x81\n0x82\n0x83\n
if (profile->qmap_mode > 1) {
// PDN-X map to qmimux-X
profile->muxid = (buf[5 * (profile->pdp - 1) + 2] - '0') * 16 +
(buf[5 * (profile->pdp - 1) + 3] - '0');
sprintf(qmap_netcard, "qmimux%d", profile->pdp - 1);
profile->qmapnet_adapter = strdup(qmap_netcard);
} else if (profile->qmap_mode == 1) {
profile->muxid =
(buf[5 * 0 + 2] - '0') * 16 + (buf[5 * 0 + 3] - '0');
sprintf(qmap_netcard, "qmimux%d", 0);
profile->qmapnet_adapter = strdup(qmap_netcard);
}
}
}
_out:
if (profile->qmap_mode) {
profile->qmap_size = 16 * 1024;
snprintf(pl->filename, sizeof(pl->filename),
"/sys/class/net/%s/qmap_size", profile->usbnet_adapter);
if (!access(pl->filename, R_OK)) {
size_t n;
char buf[32];
n = fibo_fread(pl->filename, buf, sizeof(buf));
if (n > 0) {
profile->qmap_size = atoi(buf);
}
}
}
_final_process:
if (profile->qmap_mode)
dbg_time("qmap_mode = %d, muxid = 0x%02x, qmap_netcard = %s",
profile->qmap_mode, profile->muxid, profile->qmapnet_adapter);
free(pl);
return 0;
}
int fibo_qmap_mode_set(PROFILE_T *profile)
{
int n;
char buf[128];
struct {
char filename[255 * 2];
char linkname[255 * 2];
} * pl;
if (qmidev_is_pciemhi(profile->qmichannel))
{
dbg_time("pcie mode exit fibo_qmap_mode_set ");
return 0;
}
pl = (typeof(pl))malloc(sizeof(*pl));
snprintf(pl->linkname, sizeof(pl->linkname),
"/sys/class/net/%s/device/driver", profile->usbnet_adapter);
n = readlink(pl->linkname, pl->filename, sizeof(pl->filename));
pl->filename[n] = '\0';
while (pl->filename[n] != '/') n--;
profile->driver_name = strdup(&pl->filename[n + 1]);
snprintf(pl->filename, sizeof(pl->filename), "/sys/class/net/%s/qmap_num",
profile->usbnet_adapter);
if (access(pl->filename, R_OK)) {
if (errno != ENOENT) {
dbg_time("fail to access %s, errno: %d (%s)", pl->filename, errno,
strerror(errno));
goto _out;
}
snprintf(pl->filename, sizeof(pl->filename),
"/sys/module/%s/parameters/qmap_mnum", profile->driver_name);
if (access(pl->filename, R_OK)) {
if (errno != ENOENT) {
dbg_time("fail to access %s, errno: %d (%s)", pl->filename,
errno, strerror(errno));
goto _out;
}
snprintf(
pl->filename, sizeof(pl->filename),
"/sys/class/net/%s/device/driver/module/parameters/qmap_num",
profile->usbnet_adapter);
if (access(pl->filename, R_OK)) {
if (errno != ENOENT) {
dbg_time("fail to access %s, errno: %d (%s)", pl->filename,
errno, strerror(errno));
goto _out;
}
}
}
}
if (!access(pl->filename, R_OK)) {
snprintf(buf, sizeof(buf), "%d", profile->pdpnum);
n = fibo_fwrite(pl->filename, buf, strlen(buf));
}
_out:
free(pl);
return 0;
}

View File

@ -0,0 +1,160 @@
//2021-03-24 willa.liu@fibocom.com changed begin for support mantis 0071817
#include "QMIThread.h"
#include "query_pcie_mode.h"
int speed_arr[] = { B115200, B19200, B9600, B4800, B2400, B1200, B300,//
B38400, B19200, B9600, B4800, B2400, B1200, B300,
};
int name_arr[] = {115200, 19200, 9600, 4800, 2400, 1200, 300,
38400, 19200, 9600, 4800, 2400, 1200, 300,
};
int get_pcie_mode_debug = 0;
//2021-02-24 willa.liu@fibocom.com changed begin for support eipd SN-20210129001
int get_private_gateway_debug = 1;
//2021-02-24 willa.liu@fibocom.com changed end for support eipd SN-20210129001
//static int xset1(int fd, struct termios *tio, const char *device)
int xset1(int fd, struct termios *tio, const char *device)
{
int ret = tcsetattr(fd, TCSAFLUSH, tio);
if (ret) {
printf("can't tcsetattr for %s", device);
}
return ret;
}
// set raw tty mode
//static void xget1(int fd, struct termios *t, struct termios *oldt)
void xget1(int fd, struct termios *t, struct termios *oldt)
{
tcgetattr(fd, oldt);
*t = *oldt;
cfmakeraw(t);
// t->c_lflag &= ~(ISIG|ICANON|ECHO|IEXTEN);
// t->c_iflag &= ~(BRKINT|IXON|ICRNL);
// t->c_oflag &= ~(ONLCR);
// t->c_cc[VMIN] = 1;
// t->c_cc[VTIME] = 0;
}
//static int get_pcie_mode()
int get_pcie_mode()
//2021-03-24 willa.liu@fibocom.com changed end for support mantis 0071817
{
int i;
int fd;
int ret;
char buffer[409600] = {0};
int rate;
char* sendbuffer;
int totallen = 0;
fd_set readfds;
struct timeval timeout;
struct termios tio0, tiosfd, tio;
int timeoutVal = 5;
char *dev = "/dev/ttyUSB1"; //The port under Linux is operated by opening the device file
rate = 115200;
sendbuffer = "at+gtpcie=3";
printf ( "dev: %s\nrate:%d\nsendbuffer:%s\n", dev,rate,sendbuffer);
fd = open(dev, O_RDWR | O_NOCTTY | O_NONBLOCK);
if (fd < 0)
goto ERR;
fcntl(fd, F_SETFL, O_RDWR);
// put device to "raw mode"
xget1(fd, &tio, &tiosfd);
// set device speed
for ( i = 0;i < sizeof ( speed_arr ) / sizeof ( int );i++ )
{
if ( rate == name_arr[i] ) //Judge if the pass is equal to the pass
{
break;
}
}
if(i >= sizeof ( speed_arr ) / sizeof ( int ))
{
printf("bound rate set failed\n");
goto ERR;
}
cfsetspeed(&tio, speed_arr[i]);
if (xset1(fd, &tio, dev))
goto ERR;
tcflush ( fd, TCIOFLUSH );
sprintf(buffer, "%s\r", sendbuffer);
ret = write ( fd, buffer, strlen(buffer) );
if(ret < 0)
{
printf ( "write failed\n" );
goto ERR;
}
if(get_pcie_mode_debug)printf("write %d\n", ret);
FD_ZERO ( &readfds );
FD_SET ( fd, &readfds );
while(1)
{
timeout.tv_sec = timeoutVal;
timeout.tv_usec = 0;
ret = select ( fd+1, &readfds, ( fd_set * ) 0, ( fd_set * ) 0, &timeout );
if(ret > 0)
{
ret = read ( fd, buffer+totallen, sizeof(buffer)-totallen-1 );
if(ret < 0)
{
printf ( "read failed\n" );
goto ERR;
}
if(ret == 0)
{
goto ERR;
}
totallen += ret;
buffer[totallen] = '\0';
if(get_pcie_mode_debug)printf("read %d(%s)\n", ret, &buffer[totallen-ret]);
if(totallen == sizeof(buffer)-1)
break;
if(strstr(buffer,"\nOK") || strstr(buffer,"\nERROR")
|| strstr(buffer,"\n+CME ERROR:") || strstr(buffer,"\n+CMS ERROR:"))
{
if(get_pcie_mode_debug)printf("match OK/ERROR");
if(get_pcie_mode_debug)printf("%s", buffer);
break;
}
}
else
{
printf ( "select timeout\n" );
goto ERR;
}
}
tcsetattr(fd, TCSAFLUSH, &tiosfd);
//printf("buffer:\n %s\n", buffer);
printf("%s\n", buffer);
if(strstr(buffer,"\nERROR") || strstr(buffer,"\n+CME ERROR:") || strstr(buffer,"\n+CMS ERROR:"))
goto ERR;
close ( fd );
return 0;
ERR:
if(fd > 0)
close(fd);
return -1;
}

View File

@ -0,0 +1,17 @@
#ifndef __QUERY_PCIE_MODE__
#define __QUERY_PCIE_MODE__
extern int speed_arr[14];
extern int name_arr[14];
extern int get_pcie_mode_debug;
extern int get_private_gateway_debug;
extern int xset1(int fd, struct termios *tio, const char *device);
// set raw tty mode
extern void xget1(int fd, struct termios *t, struct termios *oldt);
extern int get_pcie_mode();
#endif

289
fibocom-dial/src/udhcpc.c Normal file
View File

@ -0,0 +1,289 @@
#include <arpa/inet.h>
#include <endian.h>
#include <net/if.h>
#include <netinet/in.h>
#include <sys/select.h>
#include <sys/socket.h>
#include <sys/types.h>
#include "QMIThread.h"
#include "util.h"
static int dibbler_client_alive = 0;
static int fibo_system(const char *shell_cmd)
{
int ret = 0;
dbg_time("%s", shell_cmd);
ret = system(shell_cmd);
if (ret) {
// dbg_time("Fail to system(\"%s\") = %d, errno: %d (%s)", shell_cmd,
// ret, errno, strerror(errno));
}
return ret;
}
static void fibo_set_mtu(const char *ifname, int ifru_mtu)
{
int inet_sock;
struct ifreq ifr;
inet_sock = socket(AF_INET, SOCK_DGRAM, 0);
if (inet_sock > 0) {
strcpy(ifr.ifr_name, ifname);
if (!ioctl(inet_sock, SIOCGIFMTU, &ifr)) {
if (ifr.ifr_ifru.ifru_mtu != ifru_mtu) {
dbg_time("change mtu %d -> %d", ifr.ifr_ifru.ifru_mtu,
ifru_mtu);
ifr.ifr_ifru.ifru_mtu = ifru_mtu;
ioctl(inet_sock, SIOCSIFMTU, &ifr);
}
}
close(inet_sock);
}
}
static void *udhcpc_thread_function(void *arg)
{
FILE *udhcpc_fp;
char *udhcpc_cmd = (char *)arg;
if (udhcpc_cmd == NULL)
return NULL;
dbg_time("%s", udhcpc_cmd);
udhcpc_fp = popen(udhcpc_cmd, "r");
free(udhcpc_cmd);
if (udhcpc_fp) {
char buf[0xff];
buf[sizeof(buf) - 1] = '\0';
while ((fgets(buf, sizeof(buf) - 1, udhcpc_fp)) != NULL) {
if ((strlen(buf) > 1) && (buf[strlen(buf) - 1] == '\n'))
buf[strlen(buf) - 1] = '\0';
dbg_time("%s", buf);
}
pclose(udhcpc_fp);
}
return NULL;
}
void fibo_set_driver_link_state(PROFILE_T *profile, int link_state)
{
char link_file[128];
int fd;
int new_state = 0;
dbg_time("enter %s ", __func__);
snprintf(link_file, sizeof(link_file), "/sys/class/net/%s/link_state",
profile->usbnet_adapter);
fd = open(link_file, O_RDWR | O_NONBLOCK | O_NOCTTY);
if (fd == -1) {
if (errno != ENOENT)
dbg_time("Fail to access %s, errno: %d (%s)", link_file, errno,
strerror(errno));
return;
}
if (profile->qmap_mode <= 1)
new_state = !!link_state;
else {
// 0x80 means link off this pdp
new_state = (link_state ? 0x00 : 0x80) + profile->pdp;
}
snprintf(link_file, sizeof(link_file), "%d\n", new_state);
write(fd, link_file, sizeof(link_file));
if (link_state == 0 && profile->qmap_mode > 1) {
size_t rc;
lseek(fd, 0, SEEK_SET);
rc = read(fd, link_file, sizeof(link_file));
if (rc > 1 && (!strcasecmp(link_file, "0\n") ||
!strcasecmp(link_file, "0x0\n"))) {
// snprintf(link_file, sizeof(link_file), "busybox ifconfig %s down",
// profile->usbnet_adapter);
// fibo_system(link_file);
}
}
close(fd);
}
int fibo_raw_ip_mode_check(const char *ifname)
{
int fd;
char raw_ip[128];
char shell_cmd[128];
char mode[2] = "X";
int mode_change = 0;
snprintf(raw_ip, sizeof(raw_ip), "/sys/class/net/%s/qmi/raw_ip", ifname);
if (access(raw_ip, F_OK))
return 0;
fd = open(raw_ip, O_RDWR | O_NONBLOCK | O_NOCTTY);
if (fd < 0) {
dbg_time("%s %d fail to open(%s), errno:%d (%s)", __FILE__, __LINE__,
raw_ip, errno, strerror(errno));
return 0;
}
read(fd, mode, 2);
if (mode[0] == '0' || mode[0] == 'N') {
snprintf(shell_cmd, sizeof(shell_cmd), "busybox ifconfig %s down", ifname);
fibo_system(shell_cmd);
dbg_time("echo Y > /sys/class/net/%s/qmi/raw_ip", ifname);
mode[0] = 'Y';
write(fd, mode, 2);
mode_change = 1;
snprintf(shell_cmd, sizeof(shell_cmd), "busybox ifconfig %s up", ifname);
fibo_system(shell_cmd);
}
close(fd);
return mode_change;
}
void udhcpc_start(PROFILE_T *profile)
{
char *ifname = profile->usbnet_adapter;
char shell_cmd[128];
fibo_set_driver_link_state(profile, 1);
if (profile->qmapnet_adapter) {
ifname = profile->qmapnet_adapter;
}
dbg_time("1 %s", profile->qmichannel);
if (qmidev_is_qmiwwan(profile->qmichannel)) {
dbg_time("2 %s", ifname);
fibo_raw_ip_mode_check(ifname);
}
if (profile->rawIP && profile->ipv4.Address && profile->ipv4.Mtu) {
fibo_set_mtu(ifname, (profile->ipv4.Mtu));
}
fibo_system("echo 1 > /sys/module/fibo_mhi/parameters/macaddr_check");
//begin modified by zhangkaibo fix ipv6 dial process flow. mantis 0048789 20200605
// if (strcmp(ifname, profile->usbnet_adapter)) {
// snprintf(shell_cmd, sizeof(shell_cmd) - 1, "busybox ifconfig %s up",
// profile->usbnet_adapter);
// fibo_system(shell_cmd);
// }
//begin modified by zhangkaibo fix ipv6 dial process flow. mantis 0048789 20200605
// For IPv6, down & up will send protocol packets, and that's needed.
if (profile->ipv6_flag) {
snprintf(shell_cmd, sizeof(shell_cmd) - 1, "busybox ifconfig %s down", ifname);
fibo_system(shell_cmd);
}
snprintf(shell_cmd, sizeof(shell_cmd) - 1, "busybox ifconfig %s up", ifname);
fibo_system(shell_cmd);
//begin modified by zhangming Added NOARP and Multilcast on flag bit commands. mantis 0050106 20200713
snprintf(shell_cmd, sizeof(shell_cmd) - 1, "ip link set %s arp off multicast on", ifname);
fibo_system(shell_cmd);
//begin modified by zhangming Added NOARP and Multilcast on flag bit commands. mantis 0050106 20200713
//Modified unicom dual stack dialing unsuccessful problem
// for bridge mode, only one public IP, so do udhcpc manually
if (fibo_bridge_mode_detect(profile)) {
return;
}
/* Do DHCP using busybox tools */
{
char udhcpc_cmd[128];
pthread_attr_t udhcpc_thread_attr;
pthread_t udhcpc_thread_id;
pthread_attr_init(&udhcpc_thread_attr);
pthread_attr_setdetachstate(&udhcpc_thread_attr,
PTHREAD_CREATE_DETACHED);
if (profile->ipv4.Address) {
if (access("/usr/share/udhcpc/default.script", X_OK)) {
dbg_time(
"Fail to access /usr/share/udhcpc/default.script, "
"errno: %d (%s)",
errno, strerror(errno));
}
//-f,--foreground Run in foreground
//-b,--background Background if lease is not obtained
//-n,--now Exit if lease is not obtained
//-q,--quit Exit after obtaining lease
//-t,--retries N Send up to N discover packets (default 3)
snprintf(udhcpc_cmd, sizeof(udhcpc_cmd),
"busybox udhcpc -f -n -q -t 5 -i %s", ifname);
if (!access("/lib/netifd/dhcp.script", X_OK) &&
!access("/sbin/ifup", X_OK) &&
!access("/sbin/ifstatus", X_OK)) {
dbg_time("you are use OpenWrt?");
dbg_time("should not calling udhcpc manually?");
dbg_time("should modify /etc/config/network as below?");
dbg_time("config interface wan");
dbg_time("\toption ifname %s", ifname);
dbg_time("\toption proto dhcp");
dbg_time(
"should use \"/sbin/ifstaus wan\" to check %s 's status?",
ifname);
}
pthread_create(&udhcpc_thread_id, NULL, udhcpc_thread_function,
(void *)strdup(udhcpc_cmd));
sleep(1);
}
if (profile->ipv6.Address[0] && profile->ipv6.PrefixLengthIPAddr) {
// module do not support DHCPv6, only support 'Router Solicit'
// and it seem if enable /proc/sys/net/ipv6/conf/all/forwarding,
// Kernel do not send RS
const char *forward_file = "/proc/sys/net/ipv6/conf/all/forwarding";
int forward_fd = open(forward_file, O_RDONLY);
if (forward_fd > 0) {
char forward_state[2];
read(forward_fd, forward_state, 2);
if (forward_state[0] == '1') {
dbg_time(
"%s enabled, kernel maybe donot send 'Router Solicit'",
forward_file);
}
close(forward_fd);
}
}
}
}
void udhcpc_stop(PROFILE_T *profile)
{
char *ifname = profile->usbnet_adapter;
char shell_cmd[128];
char reset_ip[128];
dbg_time("enter %s ", __func__);
if (profile->qmapnet_adapter) {
ifname = profile->qmapnet_adapter;
}
if (dibbler_client_alive) {
system("killall dibbler-client");
dibbler_client_alive = 0;
}
//snprintf(shell_cmd, sizeof(shell_cmd) - 1, "busybox ifconfig %s down",
// profile->usbnet_adapter);
//fibo_system(shell_cmd);
snprintf(shell_cmd, sizeof(shell_cmd) - 1, "busybox ifconfig %s down", ifname);
fibo_system(shell_cmd);
snprintf(reset_ip, sizeof(reset_ip) - 1, "busybox ifconfig %s 0.0.0.0", ifname);
fibo_system(reset_ip);
}

View File

@ -0,0 +1,421 @@
#include <sys/socket.h>
#include <sys/select.h>
#include <sys/types.h>
#include <net/if.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <endian.h>
#include "libmnl/ifutils.h"
#include "libmnl/dhcp/dhcp.h"
#include "util.h"
#include "QMIThread.h"
static int fibo_system(const char *shell_cmd)
{
int ret = 0;
dbg_time("%s", shell_cmd);
ret = system(shell_cmd);
if (ret) {
// dbg_time("Fail to system(\"%s\") = %d, errno: %d (%s)", shell_cmd,
// ret, errno, strerror(errno));
}
return ret;
}
int fibo_raw_ip_mode_check(const char *ifname)
{
int fd;
char raw_ip[128];
char mode[2] = "X";
int mode_change = 0;
snprintf(raw_ip, sizeof(raw_ip), "/sys/class/net/%s/qmi/raw_ip", ifname);
if (access(raw_ip, F_OK))
return 0;
fd = open(raw_ip, O_RDWR | O_NONBLOCK | O_NOCTTY);
if (fd < 0)
{
dbg_time("%s %d fail to open(%s), errno:%d (%s)", __FILE__, __LINE__, raw_ip, errno, strerror(errno));
return 0;
}
read(fd, mode, 2);
if (mode[0] == '0' || mode[0] == 'N')
{
if_link_down(ifname);
dbg_time("echo Y > /sys/class/net/%s/qmi/raw_ip", ifname);
mode[0] = 'Y';
write(fd, mode, 2);
mode_change = 1;
if_link_up(ifname);
}
close(fd);
return mode_change;
}
static void fibo_set_driver_link_state(PROFILE_T *profile, int link_state)
{
char link_file[128];
int fd;
int new_state = 0;
dbg_time("enter %s ", __func__);
snprintf(link_file, sizeof(link_file), "/sys/class/net/%s/link_state", profile->usbnet_adapter);
fd = open(link_file, O_RDWR | O_NONBLOCK | O_NOCTTY);
if (fd == -1)
{
if (errno != ENOENT)
dbg_time("Fail to access %s, errno: %d (%s)", link_file, errno, strerror(errno));
return;
}
if (profile->qmap_mode <= 1)
new_state = !!link_state;
else
{
//0x80 means link off this pdp
new_state = (link_state ? 0x00 : 0x80) + profile->pdp;
}
snprintf(link_file, sizeof(link_file), "%d\n", new_state);
write(fd, link_file, sizeof(link_file));
if (link_state == 0 && profile->qmap_mode > 1)
{
size_t rc;
lseek(fd, 0, SEEK_SET);
rc = read(fd, link_file, sizeof(link_file));
if (rc > 1 && (!strcasecmp(link_file, "0\n") || !strcasecmp(link_file, "0x0\n")))
{
// if_link_down(profile->usbnet_adapter);
}
}
close(fd);
}
void udhcpc_start(PROFILE_T *profile)
{
//2021-02-01 willa.liu@fibocom.com changed begin for support mantis 0069837
char *ifname = profile->usbnet_adapter;
char shell_cmd[512];
fibo_set_driver_link_state(profile, 1);
fibo_raw_ip_mode_check(ifname);
if (profile->qmapnet_adapter)
{
ifname = profile->qmapnet_adapter;
}
if (profile->rawIP && profile->ipv4.Address && profile->ipv4.Mtu)
{
if_set_mtu(ifname, (profile->ipv4.Mtu));
}
if (strcmp(ifname, profile->usbnet_adapter))
{
//if_link_up(profile->usbnet_adapter);
snprintf(shell_cmd, sizeof(shell_cmd) - 1, "ifconfig %s up", profile->usbnet_adapter);
fibo_system(shell_cmd);
}
//2021-02-01 willa.liu@fibocom.com changed begin for support mantis 0069837
if_link_up(ifname);
#if 1 //for bridge mode, only one public IP, so do udhcpc manually
if (fibo_bridge_mode_detect(profile))
{
return;
}
#endif
// if use DHCP(should make with ${DHCP} src files)
// do_dhcp(ifname);
// return 0;
/* IPv4 Addr Info */
if (profile->ipv4.Address)
{
dbg_time("IPv4 MTU: %d", profile->ipv4.Mtu);
dbg_time("IPv4 Address: %s", ipaddr_to_string_v4(ntohl(profile->ipv4.Address)));
dbg_time("IPv4 Netmask: %d", mask_to_prefix_v4(ntohl(profile->ipv4.SubnetMask)));
dbg_time("IPv4 Gateway: %s", ipaddr_to_string_v4(ntohl(profile->ipv4.Gateway)));
dbg_time("IPv4 DNS1: %s", ipaddr_to_string_v4(ntohl(profile->ipv4.DnsPrimary)));
dbg_time("IPv4 DNS2: %s", ipaddr_to_string_v4(ntohl(profile->ipv4.DnsSecondary)));
if_set_network_v4(ifname, ntohl(profile->ipv4.Address),
mask_to_prefix_v4(profile->ipv4.SubnetMask),
ntohl(profile->ipv4.Gateway),
ntohl(profile->ipv4.DnsPrimary),
ntohl(profile->ipv4.DnsSecondary));
}
else
{
dbg_time("The IPv4 Address in profile is NULL");
}
if (profile->ipv6.Address && (profile->ipv6.PrefixLengthIPAddr != 0))
{
//module do not support DHCPv6, only support 'Router Solicit'
//and it seem if enable /proc/sys/net/ipv6/conf/all/forwarding, Kernel do not send RS
const char *forward_file = "/proc/sys/net/ipv6/conf/all/forwarding";
int forward_fd = open(forward_file, O_RDONLY);
if (forward_fd > 0)
{
char forward_state[2];
read(forward_fd, forward_state, 2);
if (forward_state[0] == '1')
{
dbg_time("%s enabled, kernel maybe donot send 'Router Solicit'", forward_file);
}
close(forward_fd);
}
dbg_time("IPv6 MTU: %d", profile->ipv6.Mtu);
dbg_time("IPv6 Address: %s", ipaddr_to_string_v6(profile->ipv6.Address));
dbg_time("IPv6 PrefixLengthIPAddr: %d", profile->ipv6.PrefixLengthIPAddr);
dbg_time("IPv6 Gateway: %s", ipaddr_to_string_v6(profile->ipv6.Gateway));
dbg_time("IPv6 DNS1: %s", ipaddr_to_string_v6(profile->ipv6.DnsPrimary));
dbg_time("IPv6 DNS2: %s", ipaddr_to_string_v6(profile->ipv6.DnsSecondary));
if_set_network_v6(ifname, profile->ipv6.Address, profile->ipv6.PrefixLengthIPAddr,
profile->ipv6.Gateway, profile->ipv6.DnsPrimary, profile->ipv6.DnsSecondary);
}
else
{
dbg_time("The IPv6 Address in profile is NULL");
}
}
void udhcpc_stop(PROFILE_T *profile)
{
char *ifname = profile->usbnet_adapter;
dbg_time("enter %s ", __func__);
fibo_set_driver_link_state(profile, 0);
if (profile->qmapnet_adapter)
{
ifname = profile->qmapnet_adapter;
}
if_flush_v4_addr(ifname);
if_flush_v6_addr(ifname);
if_link_down(ifname);
}
static void fibo_set_mtu(const char *ifname, int ifru_mtu) {
int inet_sock;
struct ifreq ifr;
inet_sock = socket(AF_INET, SOCK_DGRAM, 0);
if (inet_sock > 0) {
strcpy(ifr.ifr_name, ifname);
if (!ioctl(inet_sock, SIOCGIFMTU, &ifr)) {
if (ifr.ifr_ifru.ifru_mtu != ifru_mtu) {
dbg_time("change mtu %d -> %d", ifr.ifr_ifru.ifru_mtu , ifru_mtu);
ifr.ifr_ifru.ifru_mtu = ifru_mtu;
ioctl(inet_sock, SIOCSIFMTU, &ifr);
}
}
close(inet_sock);
}
}
static void* udhcpc_thread_function(void* arg) {
FILE * udhcpc_fp;
char *udhcpc_cmd = (char *)arg;
if (udhcpc_cmd == NULL)
return NULL;
dbg_time("%s", udhcpc_cmd);
udhcpc_fp = popen(udhcpc_cmd, "r");
free(udhcpc_cmd);
if (udhcpc_fp) {
char buf[0xff];
buf[sizeof(buf)-1] = '\0';
while((fgets(buf, sizeof(buf)-1, udhcpc_fp)) != NULL) {
if ((strlen(buf) > 1) && (buf[strlen(buf) - 1] == '\n'))
buf[strlen(buf) - 1] = '\0';
dbg_time("%s", buf);
}
pclose(udhcpc_fp);
}
return NULL;
}
void udhcpc_start_pcie(PROFILE_T *profile) {
char *ifname = profile->usbnet_adapter;
char sub_intf_name[100] = {0};
int intf_id = 0;
char shell_cmd[512];
dbg_time("enter %s ", __func__);
if (profile->qmapnet_adapter) {
ifname = profile->qmapnet_adapter;
}
if (profile->muxid > 0x81)
{
intf_id = profile->muxid - 0x81;
snprintf(sub_intf_name, sizeof(sub_intf_name) - 1, "%s.%d", ifname, intf_id);
}
if (profile->rawIP && profile->ipv4.Address && profile->ipv4.Mtu) {
fibo_set_mtu(ifname, (profile->ipv4.Mtu));
}
fibo_system("echo 1 > /sys/module/fibo_mhi/parameters/macaddr_check");
if (strcmp(ifname, profile->usbnet_adapter)) {
snprintf(shell_cmd, sizeof(shell_cmd) - 1, "ifconfig %s up", profile->usbnet_adapter);
fibo_system(shell_cmd);
}
// For IPv6, down & up will send protocol packets, and that's needed.
if (profile->ipv6_flag && profile->muxid <= 0x81) {
snprintf(shell_cmd, sizeof(shell_cmd) - 1, "ifconfig %s down", ifname);
fibo_system(shell_cmd);
}
snprintf(shell_cmd, sizeof(shell_cmd) - 1, "ifconfig %s up", ifname);
fibo_system(shell_cmd);
//for bridge mode, only one public IP, so do udhcpc manually
if (fibo_bridge_mode_detect(profile)) {
return;
}
/* Do DHCP using busybox tools */
{
char udhcpc_cmd[128];
pthread_attr_t udhcpc_thread_attr;
pthread_t udhcpc_thread_id;
pthread_attr_init(&udhcpc_thread_attr);
pthread_attr_setdetachstate(&udhcpc_thread_attr, PTHREAD_CREATE_DETACHED);
if (profile->ipv4.Address)
{
char v4add_str[32] = {0};
char v4gw_str[32] = {0};
char v4_netmask_str[32] = {0};
uint32_t Address = ntohl(profile->ipv4.Address);
uint32_t Gateway = ntohl(profile->ipv4.Gateway);
uint32_t SubnetMask = ntohl(profile->ipv4.SubnetMask);
inet_ntop(AF_INET, &Address, v4add_str, sizeof(v4add_str));
inet_ntop(AF_INET, &Gateway, v4gw_str, sizeof(v4gw_str));
inet_ntop(AF_INET, &SubnetMask, v4_netmask_str, sizeof(v4_netmask_str));
if (profile->muxid == 0x81)
{
snprintf(shell_cmd, sizeof(shell_cmd) - 1, "ifconfig %s %s netmask %s", ifname, v4add_str, v4_netmask_str);
fibo_system(shell_cmd);
snprintf(shell_cmd, sizeof(shell_cmd) - 1, "route add default gw %s dev %s", v4gw_str, ifname);
fibo_system(shell_cmd);
}
else if (profile->muxid > 0x81)
{
snprintf(shell_cmd, sizeof(shell_cmd) - 1, "ip link add link %s name %s type vlan id %d", ifname, sub_intf_name, intf_id);
fibo_system(shell_cmd);
snprintf(shell_cmd, sizeof(shell_cmd) - 1, "ip link set %s up", ifname);
fibo_system(shell_cmd);
snprintf(shell_cmd, sizeof(shell_cmd) - 1, "ip link set %s up", sub_intf_name);
fibo_system(shell_cmd);
snprintf(shell_cmd, sizeof(shell_cmd) - 1, "ifconfig %s %s netmask %s", sub_intf_name, v4add_str, v4_netmask_str);
fibo_system(shell_cmd);
}
}
if (profile->ipv6.PrefixLengthIPAddr)
{
//module do not support DHCPv6, only support 'Router Solicit'
//and it seem if enable /proc/sys/net/ipv6/conf/all/forwarding, Kernel do not send RS
const char *forward_file = "/proc/sys/net/ipv6/conf/all/forwarding";
int forward_fd = open(forward_file, O_RDONLY);
if (forward_fd > 0) {
char forward_state[2];
read(forward_fd, forward_state, 2);
if (forward_state[0] == '1') {
dbg_time("%s enabled, kernel maybe donot send 'Router Solicit'", forward_file);
}
close(forward_fd);
}
{
char v6add_str[100] = {0};
char v6gw_str[100] = {0};
inet_ntop(AF_INET6, profile->ipv6.Address, v6add_str, sizeof(v6add_str));
inet_ntop(AF_INET6, profile->ipv6.Gateway, v6gw_str, sizeof(v6gw_str));
if (profile->muxid == 0x81)
{
snprintf(shell_cmd, sizeof(shell_cmd) - 1, "ifconfig %s add %s/%d", ifname, v6add_str, profile->ipv6.PrefixLengthIPAddr);
fibo_system(shell_cmd);
snprintf(shell_cmd, sizeof(shell_cmd) - 1, "route -A inet6 add ::/0 gw %s dev %s", v6gw_str, ifname);
fibo_system(shell_cmd);
}
else if (profile->muxid > 0x81)
{
snprintf(shell_cmd, sizeof(shell_cmd) - 1, "ip link add link %s name %s type vlan id %d", ifname, sub_intf_name, intf_id);
fibo_system(shell_cmd);
snprintf(shell_cmd, sizeof(shell_cmd) - 1, "ip link set %s up", ifname);
fibo_system(shell_cmd);
snprintf(shell_cmd, sizeof(shell_cmd) - 1, "ip link set %s up", sub_intf_name);
fibo_system(shell_cmd);
snprintf(shell_cmd, sizeof(shell_cmd) - 1, "ifconfig %s add %s/%d", sub_intf_name, v6add_str, profile->ipv6.PrefixLengthIPAddr);
fibo_system(shell_cmd);
/* start 2021-01-21 add by haopengfei to fix mantis 69056 */
snprintf(shell_cmd, sizeof(shell_cmd) - 1, "route -A inet6 add ::/0 gw %s dev %s", v6gw_str, sub_intf_name);
fibo_system(shell_cmd);
/* end 2021-01-21 add by haopengfei to fix mantis 69056 */
}
}
}
}
}
void udhcpc_stop_pcie(PROFILE_T *profile) {
char *ifname = profile->usbnet_adapter;
char shell_cmd[128];
char reset_ip[128];
dbg_time("enter %s ", __func__);
if (profile->qmapnet_adapter) {
ifname = profile->qmapnet_adapter;
}
if (profile->muxid == 0x81)
{
snprintf(shell_cmd, sizeof(shell_cmd) - 1, "ifconfig %s down", ifname);
fibo_system(shell_cmd);
snprintf(reset_ip, sizeof(reset_ip) - 1, "ifconfig %s 0.0.0.0", ifname);
fibo_system(reset_ip);
}
else if (profile->muxid > 0x81)
{
char sub_intf_name[100];
int intf_id = profile->muxid - 0x81;
snprintf(sub_intf_name, sizeof(sub_intf_name) - 1, "%s.%d", ifname, intf_id);
snprintf(shell_cmd, sizeof(shell_cmd) - 1, "ip link del link dev %s", sub_intf_name);
fibo_system(shell_cmd);
}
}

142
fibocom-dial/src/util.c Normal file
View File

@ -0,0 +1,142 @@
#include <sys/time.h>
#include "QMIThread.h"
#if defined(__STDC__)
#include <stdarg.h>
#define __V(x) x
#else
#include <varargs.h>
#define __V(x) (va_alist) va_dcl
#define const
#define volatile
#endif
#include <syslog.h>
#define is_bigendian() ((*(char *)&i) == 0)
FILE *logfilefp = NULL;
static pthread_mutex_t printfMutex = PTHREAD_MUTEX_INITIALIZER;
static char line[1024];
const int i = 1;
// defined in atchannel.c
static void setTimespecRelative(struct timespec *p_ts, long long msec)
{
struct timeval tv;
gettimeofday(&tv, (struct timezone *)NULL);
/* what's really funny about this is that I know
pthread_cond_timedwait just turns around and makes this
a relative time again */
p_ts->tv_sec = tv.tv_sec + (msec / 1000);
p_ts->tv_nsec = (tv.tv_usec + (msec % 1000) * 1000L) * 1000L;
}
int pthread_cond_timeout_np(pthread_cond_t *cond, pthread_mutex_t *mutex,
unsigned msecs)
{
if (msecs != 0) {
struct timespec ts;
setTimespecRelative(&ts, msecs);
return pthread_cond_timedwait(cond, mutex, &ts);
} else {
return pthread_cond_wait(cond, mutex);
}
}
static const char *get_time(void)
{
static char time_buf[50];
struct timeval tv;
time_t time;
suseconds_t millitm;
struct tm *ti;
gettimeofday(&tv, NULL);
time = tv.tv_sec;
millitm = (tv.tv_usec + 500) / 1000;
if (millitm == 1000) {
++time;
millitm = 0;
}
ti = localtime(&time);
sprintf(time_buf, "[%02d-%02d_%02d:%02d:%02d:%03d]", ti->tm_mon + 1,
ti->tm_mday, ti->tm_hour, ti->tm_min, ti->tm_sec, (int)millitm);
return time_buf;
}
void dbg_time(const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
pthread_mutex_lock(&printfMutex);
snprintf(line, sizeof(line), "%s ", get_time());
vsnprintf(line + strlen(line), sizeof(line) - strlen(line), fmt, args);
fprintf(stdout, "%s\n", line);
if (logfilefp) {
fprintf(logfilefp, "%s\n", line);
}
fflush(logfilefp);
pthread_mutex_unlock(&printfMutex);
}
USHORT le16_to_cpu(USHORT v16)
{
USHORT tmp = v16;
if (is_bigendian()) {
unsigned char *s = (unsigned char *)(&v16);
unsigned char *d = (unsigned char *)(&tmp);
d[0] = s[1];
d[1] = s[0];
}
return tmp;
}
UINT le32_to_cpu(UINT v32)
{
UINT tmp = v32;
if (is_bigendian()) {
unsigned char *s = (unsigned char *)(&v32);
unsigned char *d = (unsigned char *)(&tmp);
d[0] = s[3];
d[1] = s[2];
d[2] = s[1];
d[3] = s[0];
}
return tmp;
}
USHORT cpu_to_le16(USHORT v16)
{
USHORT tmp = v16;
if (is_bigendian()) {
unsigned char *s = (unsigned char *)(&v16);
unsigned char *d = (unsigned char *)(&tmp);
d[0] = s[1];
d[1] = s[0];
}
return tmp;
}
UINT cpu_to_le32(UINT v32)
{
UINT tmp = v32;
if (is_bigendian()) {
unsigned char *s = (unsigned char *)(&v32);
unsigned char *d = (unsigned char *)(&tmp);
d[0] = s[3];
d[1] = s[2];
d[2] = s[1];
d[3] = s[0];
}
return tmp;
}

53
fibocom-dial/src/util.h Normal file
View File

@ -0,0 +1,53 @@
/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef _UTILS_H_
#define _UTILS_H_
#include <stddef.h>
struct listnode {
struct listnode *next;
struct listnode *prev;
};
#define node_to_item(node, container, member) \
(container *)(((char *)(node)) - offsetof(container, member))
#define list_declare(name) \
struct listnode name = { \
.next = &name, \
.prev = &name, \
}
#define list_for_each(node, list) \
for (node = (list)->next; node != (list); node = node->next)
#define list_for_each_reverse(node, list) \
for (node = (list)->prev; node != (list); node = node->prev)
void dbg_time(const char *fmt, ...);
void list_init(struct listnode *list);
void list_add_tail(struct listnode *list, struct listnode *item);
void list_add_head(struct listnode *head, struct listnode *item);
void list_remove(struct listnode *item);
#define list_empty(list) ((list) == (list)->next)
#define list_head(list) ((list)->next)
#define list_tail(list) ((list)->prev)
int epoll_register(int epoll_fd, int fd, unsigned int events);
int epoll_deregister(int epoll_fd, int fd);
#endif

47
fibocom_QMI_WWAN/Makefile Normal file
View File

@ -0,0 +1,47 @@
#
# Copyright (C) 2015 OpenWrt.org
#
# This is free software, licensed under the GNU General Public License v2.
# See /LICENSE for more information.
#
include $(TOPDIR)/rules.mk
PKG_NAME:=qmi_wwan_f
PKG_VERSION:=1.0
PKG_RELEASE:=2
include $(INCLUDE_DIR)/kernel.mk
include $(INCLUDE_DIR)/package.mk
define KernelPackage/qmi_wwan_f
SUBMENU:=WWAN Support
TITLE:=Fibocom Linux USB QMI WWAN Driver
DEPENDS:=+kmod-usb-net +kmod-usb-wdm
FILES:=$(PKG_BUILD_DIR)/qmi_wwan_f.ko
AUTOLOAD:=$(call AutoLoad,82,qmi_wwan_f)
endef
define KernelPackage/qmi_wwan_f/description
Fibocom Linux USB QMI WWAN Driver
endef
MAKE_OPTS:= \
ARCH="$(LINUX_KARCH)" \
CROSS_COMPILE="$(TARGET_CROSS)" \
CXXFLAGS="$(TARGET_CXXFLAGS)" \
M="$(PKG_BUILD_DIR)" \
$(EXTRA_KCONFIG)
define Build/Prepare
mkdir -p $(PKG_BUILD_DIR)
$(CP) ./src/* $(PKG_BUILD_DIR)/
endef
define Build/Compile
$(MAKE) -C "$(LINUX_DIR)" \
$(MAKE_OPTS) \
modules
endef
$(eval $(call KernelPackage,qmi_wwan_f))

View File

@ -0,0 +1,38 @@
obj-m += qmi_wwan_f.o
PWD := $(shell pwd)
OUTPUTDIR=/lib/modules/`uname -r`/kernel/drivers/net/usb/
ifeq ($(ARCH),)
ARCH := $(shell uname -m)
endif
ifeq ($(CROSS_COMPILE),)
CROSS_COMPILE :=
endif
ifeq ($(KDIR),)
KDIR := /lib/modules/$(shell uname -r)/build
ifeq ($(ARCH),i686)
ifeq ($(wildcard $KDIR/arch/$ARCH),)
ARCH=i386
endif
endif
endif
ifneq ($(findstring &,${PWD}),)
$(warning "${PWD}")
$(warning "current directory contain special char '&' !")
$(error "please remove it!")
endif
default:
$(MAKE) ARCH=${ARCH} CROSS_COMPILE=${CROSS_COMPILE} -C $(KDIR) M=$(PWD) modules
install: default
cp $(PWD)/qmi_wwan_f.ko /lib/modules/$(shell uname -r)/kernel/drivers/net/usb/
depmod
modprobe -r qmi_wwan_f
modprobe -r qmi_wwan
modprobe qmi_wwan_f
clean:
rm -rf *~ .tmp_versions modules.order Module.symvers
find . -type f -name "*~" -o -name "*.o" -o -name "*.ko" -o -name "*.cmd" -o -name "*.mod.c" | xargs rm -rf

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,20 @@
#
# Copyright (C) 2015 OpenWrt.org
#
# This is free software, licensed under the GNU General Public License v2.
# See /LICENSE for more information.
#
include $(TOPDIR)/rules.mk
LUCI_TITLE:=Modem Server
LUCI_DEPENDS:=+luci-compat +kmod-usb-net +kmod-usb-net-cdc-ether +kmod-usb-acm \
+kmod-usb-net-qmi-wwan +kmod-usb-net-rndis +kmod-usb-serial-qualcomm \
+kmod-usb-net-sierrawireless +kmod-usb-ohci +kmod-usb-serial \
+kmod-usb-serial-option \
+kmod-usb2 +kmod-usb3 \
+quectel-CM-5G +kmod-gobinet
include $(TOPDIR)/feeds/luci/luci.mk
# call BuildPackage - OpenWrt buildroot signature

View File

@ -0,0 +1,9 @@
module("luci.controller.gobinetmodem", package.seeall)
function index()
if not nixio.fs.access("/etc/config/gobinetmodem") then
return
end
entry({"admin", "network", "gobinetmodem"}, cbi("gobinetmodem"), _("Gobinet Modem Server"), 80).dependent = false
end

View File

@ -0,0 +1,39 @@
-- Copyright 2016 David Thornley <david.thornley@touchstargroup.com>
-- Licensed to the public under the Apache License 2.0.
mp = Map("gobinetmodem")
mp.title = translate("gobinet Modem Server")
mp.description = translate("Modem Server For OpenWrt")
s = mp:section(TypedSection, "service", "Base Setting")
s.anonymous = true
enabled = s:option(Flag, "enabled", translate("Enable"))
enabled.default = 0
enabled.rmempty = false
apn = s:option(Value, "apn", translate("APN"))
apn.rmempty = true
pincode = s:option(Value, "pincode", translate("PIN"))
pincode.rmempty = true
username = s:option(Value, "username", translate("PAP/CHAP username"))
username.rmempty = true
password = s:option(Value, "password", translate("PAP/CHAP password"))
password.rmempty = true
auth = s:option(Value, "auth", translate("Authentication Type"))
auth.rmempty = true
auth:value("", translate("-- Please choose --"))
auth:value("both", "PAP/CHAP (both)")
auth:value("pap", "PAP")
auth:value("chap", "CHAP")
auth:value("none", "NONE")
tool = s:option(Value, "tool", translate("Tools"))
tool:value("quectel-CM", "quectel-CM")
tool.rmempty = true
return mp

View File

@ -0,0 +1,24 @@
msgid ""
msgstr ""
"Project-Id-Version: \n"
"POT-Creation-Date: \n"
"PO-Revision-Date: \n"
"Last-Translator: dingpengyu <dingpengyu06@gmail.com>\n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: zh_CN\n"
"X-Generator: Poedit 2.3.1\n"
msgid "Base Setting"
msgstr "基本设置"
msgid "gobinet Modem Server"
msgstr "gobinet移动网络拨号服务"
msgid "Modem Server For OpenWrt"
msgstr "OpenWrt移动网络拨号服务"
msgid "Tools"
msgstr "拨号工具"

View File

@ -0,0 +1,4 @@
config service
option tool 'quectel-CM'
option enabled '0'

View File

@ -0,0 +1,80 @@
#!/bin/sh /etc/rc.common
# Copyright (C) 2006-2014 OpenWrt.org
START=99
STOP=16
USE_PROCD=1
#使用procd启动
run_4g()
{
local enabled
config_get_bool enabled $1 enabled
echo "run 4G" >> /tmp/log4g
if [ "$enabled" = "1" ]; then
local user
local password
local apn
local auth
local pincode
local device
local tool
# echo "enable 4G" >> /tmp/log4g
config_get user $1 user
config_get password $1 password
config_get apn $1 apn
config_get auth $1 auth
config_get pincode $1 pincode
config_get device $1 device
config_get tool $1 tool
config_get tty $1 tty
config_get atcmd $1 atcmd
devname="$(basename "$device")"
devpath="$(readlink -f /sys/class/usbmisc/$devname/device/)"
ifname="$( ls "$devpath"/net )"
if [ "$tool" = "at" ];then
at_tool "$atcmd" -d $tty
else
procd_open_instance
#创建一个实例, 在procd看来一个应用程序可以多个实\E4\BE?
#ubus call service list 可以查看实例
procd_set_param command $tool -i $ifname -s $apn
if [ "$password" != "" ];then
procd_append_param command $user $password $auth
fi
if [ "$pincode" != "" ]; then
procd_append_param command -p $pincode
fi
# procd_append_param command -f /tmp/4g.log
procd_set_param respawn
echo "quectel-CM has started."
procd_close_instance
#关闭实例
fi
fi
}
service_triggers()
{
procd_add_reload_trigger "gobinetmodem"
}
start_service() {
config_load gobinetmodem
config_foreach run_4g service
}
stop_service()
{
echo "4G stop" >> /tmp/log4g
killall quectel-CM
echo "quectel-CM has stoped."
}

View File

@ -0,0 +1,11 @@
#!/bin/sh
uci -q batch <<-EOF >/dev/null
delete ucitrack.@gobinetmodem[-1]
add ucitrack gobinetmodem
set ucitrack.@gobinetmodem[-1].init=gobinetmodem
commit ucitrack
EOF
rm -f /tmp/luci-indexcache
exit 0

View File

@ -0,0 +1,15 @@
#
# Copyright (C) 2015 OpenWrt.org
#
# This is free software, licensed under the GNU General Public License v2.
# See /LICENSE for more information.
#
include $(TOPDIR)/rules.mk
LUCI_TITLE:=PCI Modem Server
LUCI_DEPENDS:=+kmod-pcie_mhi +pciutils +quectel-CM-5G
include $(TOPDIR)/feeds/luci/luci.mk
# call BuildPackage - OpenWrt buildroot signature

View File

@ -0,0 +1,9 @@
module("luci.controller.pcimodem", package.seeall)
function index()
if not nixio.fs.access("/etc/config/pcimodem") then
return
end
entry({"admin", "network", "pcimodem"}, cbi("pcimodem"), _("PCI Modem Server"), 80).dependent = false
end

View File

@ -0,0 +1,39 @@
-- Copyright 2016 David Thornley <david.thornley@touchstargroup.com>
-- Licensed to the public under the Apache License 2.0.
mp = Map("pcimodem")
mp.title = translate("PCI Modem Server")
mp.description = translate("Modem Server For OpenWrt")
s = mp:section(TypedSection, "service", "Base Setting")
s.anonymous = true
enabled = s:option(Flag, "enabled", translate("Enable"))
enabled.default = 0
enabled.rmempty = false
apn = s:option(Value, "apn", translate("APN"))
apn.rmempty = true
pincode = s:option(Value, "pincode", translate("PIN"))
pincode.rmempty = true
username = s:option(Value, "username", translate("PAP/CHAP username"))
username.rmempty = true
password = s:option(Value, "password", translate("PAP/CHAP password"))
password.rmempty = true
auth = s:option(Value, "auth", translate("Authentication Type"))
auth.rmempty = true
auth:value("", translate("-- Please choose --"))
auth:value("both", "PAP/CHAP (both)")
auth:value("pap", "PAP")
auth:value("chap", "CHAP")
auth:value("none", "NONE")
tool = s:option(Value, "tool", translate("Tools"))
tool:value("quectel-CM", "quectel-CM")
tool.rmempty = true
return mp

View File

@ -0,0 +1,24 @@
msgid ""
msgstr ""
"Project-Id-Version: \n"
"POT-Creation-Date: \n"
"PO-Revision-Date: \n"
"Last-Translator: dingpengyu <dingpengyu06@gmail.com>\n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: zh_CN\n"
"X-Generator: Poedit 2.3.1\n"
msgid "Base Setting"
msgstr "基本设置"
msgid "PCI Modem Server"
msgstr "PCI移动网络拨号服务"
msgid "Modem Server For OpenWrt"
msgstr "OpenWrt移动网络拨号服务"
msgid "Tools"
msgstr "拨号工具"

View File

@ -0,0 +1,4 @@
config service
option tool 'quectel-CM'
option enabled '0'

View File

@ -0,0 +1,75 @@
#!/bin/sh /etc/rc.common
# Copyright (C) 2006-2014 OpenWrt.org
START=99
STOP=16
USE_PROCD=1
#使用procd启动
run_5g()
{
local enabled
config_get_bool enabled $1 enabled
echo "run 4G" >> /tmp/log4g
if [ "$enabled" = "1" ]; then
local user
local password
local apn
local auth
local pincode
local tool
# echo "enable 5G" >> /tmp/log5g
config_get user $1 user
config_get password $1 password
config_get apn $1 apn
config_get auth $1 auth
config_get pincode $1 pincode
config_get tool $1 tool
config_get tty $1 tty
config_get atcmd $1 atcmd
if [ "$tool" = "at" ];then
at_tool "$atcmd" -d $tty
else
procd_open_instance
#创建一个实例, 在procd看来一个应用程序可以多个实\E4\BE?
#ubus call service list 可以查看实例
procd_set_param command $tool -i rmnet_mhi0 -s $apn
if [ "$password" != "" ];then
procd_append_param command $user $password $auth
fi
if [ "$pincode" != "" ]; then
procd_append_param command -p $pincode
fi
# procd_append_param command -f /tmp/4g.log
procd_set_param respawn
echo "quectel-CM has started."
procd_close_instance
#关闭实例
fi
fi
}
service_triggers()
{
procd_add_reload_trigger "pcimodem"
}
start_service() {
config_load pcimodem
config_foreach run_5g service
}
stop_service()
{
echo "5G stop" >> /tmp/log5g
killall quectel-CM
echo "quectel-CM has stoped."
}

View File

@ -0,0 +1,11 @@
#!/bin/sh
uci -q batch <<-EOF >/dev/null
delete ucitrack.@pcimodem[-1]
add ucitrack pcimodem
set ucitrack.@pcimodem[-1].init=pcimodem
commit ucitrack
EOF
rm -f /tmp/luci-indexcache
exit 0

View File

@ -0,0 +1,15 @@
#
# Copyright (C) 2015 OpenWrt.org
#
# This is free software, licensed under the GNU General Public License v2.
# See /LICENSE for more information.
#
include $(TOPDIR)/rules.mk
LUCI_TITLE:=SPD Modem Server
LUCI_DEPENDS:=+luci-compat +kmod-sprd_pcie
include $(TOPDIR)/feeds/luci/luci.mk
# call BuildPackage - OpenWrt buildroot signature

View File

@ -0,0 +1,9 @@
module("luci.controller.spdmodem", package.seeall)
function index()
if not nixio.fs.access("/etc/config/spdmodem") then
return
end
entry({"admin", "network", "spdmodem"}, cbi("spdmodem"), _("SPD Modem Server"), 80).dependent = false
end

View File

@ -0,0 +1,39 @@
-- Copyright 2016 David Thornley <david.thornley@touchstargroup.com>
-- Licensed to the public under the Apache License 2.0.
mp = Map("spdmodem")
mp.title = translate("SPD Modem Server")
mp.description = translate("Modem Server For OpenWrt")
s = mp:section(TypedSection, "service", "Base Setting")
s.anonymous = true
enabled = s:option(Flag, "enabled", translate("Enable"))
enabled.default = 0
enabled.rmempty = false
apn = s:option(Value, "apn", translate("APN"))
apn.rmempty = true
pincode = s:option(Value, "pincode", translate("PIN"))
pincode.rmempty = true
username = s:option(Value, "username", translate("PAP/CHAP username"))
username.rmempty = true
password = s:option(Value, "password", translate("PAP/CHAP password"))
password.rmempty = true
auth = s:option(Value, "auth", translate("Authentication Type"))
auth.rmempty = true
auth:value("", translate("-- Please choose --"))
auth:value("both", "PAP/CHAP (both)")
auth:value("pap", "PAP")
auth:value("chap", "CHAP")
auth:value("none", "NONE")
tool = s:option(Value, "tool", translate("Tools"))
tool:value("quectel-CM", "quectel-CM")
tool.rmempty = true
return mp

View File

@ -0,0 +1,24 @@
msgid ""
msgstr ""
"Project-Id-Version: \n"
"POT-Creation-Date: \n"
"PO-Revision-Date: \n"
"Last-Translator: dingpengyu <dingpengyu06@gmail.com>\n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: zh_CN\n"
"X-Generator: Poedit 2.3.1\n"
msgid "Base Setting"
msgstr "基本设置"
msgid "SPD Modem Server"
msgstr "SPD移动网络拨号服务"
msgid "Modem Server For OpenWrt"
msgstr "OpenWrt移动网络拨号服务"
msgid "Tools"
msgstr "拨号工具"

View File

@ -0,0 +1,4 @@
config service
option tool 'quectel-CM'
option enabled '0'

View File

@ -0,0 +1,75 @@
#!/bin/sh /etc/rc.common
# Copyright (C) 2006-2014 OpenWrt.org
START=99
STOP=16
USE_PROCD=1
#使用procd启动
run_5g()
{
local enabled
config_get_bool enabled $1 enabled
echo "run 4G" >> /tmp/log4g
if [ "$enabled" = "1" ]; then
local user
local password
local apn
local auth
local pincode
local tool
# echo "enable 5G" >> /tmp/log5g
config_get user $1 user
config_get password $1 password
config_get apn $1 apn
config_get auth $1 auth
config_get pincode $1 pincode
config_get tool $1 tool
config_get tty $1 tty
config_get atcmd $1 atcmd
if [ "$tool" = "at" ];then
at_tool "$atcmd" -d $tty
else
procd_open_instance
#创建一个实例, 在procd看来一个应用程序可以多个实\E4\BE?
#ubus call service list 可以查看实例
procd_set_param command $tool -i -s $apn
if [ "$password" != "" ];then
procd_append_param command $user $password $auth
fi
if [ "$pincode" != "" ]; then
procd_append_param command -p $pincode
fi
# procd_append_param command -f /tmp/4g.log
procd_set_param respawn
echo "quectel-CM has started."
procd_close_instance
#关闭实例
fi
fi
}
service_triggers()
{
procd_add_reload_trigger "spdmodem"
}
start_service() {
config_load spdmodem
config_foreach run_5g service
}
stop_service()
{
echo "5G stop" >> /tmp/log5g
killall quectel-CM
echo "quectel-CM has stoped."
}

View File

@ -0,0 +1,11 @@
#!/bin/sh
uci -q batch <<-EOF >/dev/null
delete ucitrack.@spdmodem[-1]
add ucitrack spdmodem
set ucitrack.@spdmodem[-1].init=spdmodem
commit ucitrack
EOF
rm -f /tmp/luci-indexcache
exit 0

View File

@ -0,0 +1,20 @@
#
# Copyright (C) 2015 OpenWrt.org
#
# This is free software, licensed under the GNU General Public License v2.
# See /LICENSE for more information.
#
include $(TOPDIR)/rules.mk
LUCI_TITLE:=Modem Server
LUCI_DEPENDS:=+luci-compat +kmod-usb-net +kmod-usb-net-cdc-ether +kmod-usb-acm \
+kmod-usb-net-qmi-wwan +kmod-usb-net-rndis +kmod-usb-serial-qualcomm \
+kmod-usb-net-sierrawireless +kmod-usb-ohci +kmod-usb-serial \
+kmod-usb-serial-option +kmod-usb-wdm \
+kmod-usb2 +kmod-usb3 \
+quectel-CM-5G +kmod-qmi_wwan_q +kmod-usb-net-cdc-mbim
include $(TOPDIR)/feeds/luci/luci.mk
# call BuildPackage - OpenWrt buildroot signature

View File

@ -0,0 +1,9 @@
module("luci.controller.usbmodem", package.seeall)
function index()
if not nixio.fs.access("/etc/config/usbmodem") then
return
end
entry({"admin", "network", "usbmodem"}, cbi("usbmodem"), _("USB Modem Server"), 80).dependent = false
end

View File

@ -0,0 +1,51 @@
-- Copyright 2016 David Thornley <david.thornley@touchstargroup.com>
-- Licensed to the public under the Apache License 2.0.
mp = Map("usbmodem")
mp.title = translate("USB Modem Server")
mp.description = translate("Modem Server For OpenWrt")
s = mp:section(TypedSection, "service", "Base Setting")
s.anonymous = true
enabled = s:option(Flag, "enabled", translate("Enable"))
enabled.default = 0
enabled.rmempty = false
device = s:option(Value, "device", translate("Modem device"))
device.rmempty = false
local device_suggestions = nixio.fs.glob("/dev/cdc-wdm*")
if device_suggestions then
local node
for node in device_suggestions do
device:value(node)
end
end
apn = s:option(Value, "apn", translate("APN"))
apn.rmempty = true
pincode = s:option(Value, "pincode", translate("PIN"))
pincode.rmempty = true
username = s:option(Value, "username", translate("PAP/CHAP username"))
username.rmempty = true
password = s:option(Value, "password", translate("PAP/CHAP password"))
password.rmempty = true
auth = s:option(Value, "auth", translate("Authentication Type"))
auth.rmempty = true
auth:value("", translate("-- Please choose --"))
auth:value("both", "PAP/CHAP (both)")
auth:value("pap", "PAP")
auth:value("chap", "CHAP")
auth:value("none", "NONE")
tool = s:option(Value, "tool", translate("Tools"))
tool:value("quectel-CM", "quectel-CM")
tool.rmempty = true
return mp

View File

@ -0,0 +1,24 @@
msgid ""
msgstr ""
"Project-Id-Version: \n"
"POT-Creation-Date: \n"
"PO-Revision-Date: \n"
"Last-Translator: dingpengyu <dingpengyu06@gmail.com>\n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: zh_CN\n"
"X-Generator: Poedit 2.3.1\n"
msgid "Base Setting"
msgstr "基本设置"
msgid "USB Modem Server"
msgstr "USB移动网络拨号服务"
msgid "Modem Server For OpenWrt"
msgstr "OpenWrt移动网络拨号服务"
msgid "Tools"
msgstr "拨号工具"

View File

@ -0,0 +1,5 @@
config service
option tool 'quectel-CM'
option device '/dev/cdc-wdm0'
option enabled '0'

View File

@ -0,0 +1,80 @@
#!/bin/sh /etc/rc.common
# Copyright (C) 2006-2014 OpenWrt.org
START=99
STOP=16
USE_PROCD=1
#使用procd启动
run_4g()
{
local enabled
config_get_bool enabled $1 enabled
echo "run 4G" >> /tmp/log4g
if [ "$enabled" = "1" ]; then
local user
local password
local apn
local auth
local pincode
local device
local tool
# echo "enable 4G" >> /tmp/log4g
config_get user $1 user
config_get password $1 password
config_get apn $1 apn
config_get auth $1 auth
config_get pincode $1 pincode
config_get device $1 device
config_get tool $1 tool
config_get tty $1 tty
config_get atcmd $1 atcmd
devname="$(basename "$device")"
devpath="$(readlink -f /sys/class/usbmisc/$devname/device/)"
ifname="$( ls "$devpath"/net )"
if [ "$tool" = "at" ];then
at_tool "$atcmd" -d $tty
else
procd_open_instance
#创建一个实例, 在procd看来一个应用程序可以多个实\E4\BE?
#ubus call service list 可以查看实例
procd_set_param command $tool -i $ifname -s $apn
if [ "$password" != "" ];then
procd_append_param command $user $password $auth
fi
if [ "$pincode" != "" ]; then
procd_append_param command -p $pincode
fi
# procd_append_param command -f /tmp/4g.log
procd_set_param respawn
echo "quectel-CM has started."
procd_close_instance
#关闭实例
fi
fi
}
service_triggers()
{
procd_add_reload_trigger "usbmodem"
}
start_service() {
config_load usbmodem
config_foreach run_4g service
}
stop_service()
{
echo "4G stop" >> /tmp/log4g
killall quectel-CM
echo "quectel-CM has stoped."
}

View File

@ -0,0 +1,11 @@
#!/bin/sh
uci -q batch <<-EOF >/dev/null
delete ucitrack.@usbmodem[-1]
add ucitrack usbmodem
set ucitrack.@usbmodem[-1].init=usbmodem
commit ucitrack
EOF
rm -f /tmp/luci-indexcache
exit 0

47
quectel_Gobinet/Makefile Normal file
View File

@ -0,0 +1,47 @@
#
# Copyright (C) 2015 OpenWrt.org
#
# This is free software, licensed under the GNU General Public License v2.
# See /LICENSE for more information.
#
include $(TOPDIR)/rules.mk
PKG_NAME:=gobinet
PKG_VERSION:=1.6.3
PKG_RELEASE:=1
include $(INCLUDE_DIR)/kernel.mk
include $(INCLUDE_DIR)/package.mk
define KernelPackage/gobinet
SUBMENU:=WWAN Support
TITLE:=Quectel Linux USB Gobinet Driver
DEPENDS:=+kmod-usb-net
FILES:=$(PKG_BUILD_DIR)/GobiNet.ko
AUTOLOAD:=$(call AutoLoad,81,GobiNet)
endef
define KernelPackage/gobinet/description
Quectel Linux USB gobinet Driver
endef
MAKE_OPTS:= \
ARCH="$(LINUX_KARCH)" \
CROSS_COMPILE="$(TARGET_CROSS)" \
CXXFLAGS="$(TARGET_CXXFLAGS)" \
M="$(PKG_BUILD_DIR)" \
$(EXTRA_KCONFIG)
define Build/Prepare
mkdir -p $(PKG_BUILD_DIR)
$(CP) ./src/* $(PKG_BUILD_DIR)/
endef
define Build/Compile
$(MAKE) -C "$(LINUX_DIR)" \
$(MAKE_OPTS) \
modules
endef
$(eval $(call KernelPackage,gobinet))

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,43 @@
obj-m := GobiNet.o
GobiNet-objs := GobiUSBNet.o QMIDevice.o QMI.o
PWD := $(shell pwd)
OUTPUTDIR=/lib/modules/`uname -r`/kernel/drivers/net/usb/
ifeq ($(ARCH),)
ARCH := $(shell uname -m)
endif
ifeq ($(CROSS_COMPILE),)
CROSS_COMPILE :=
endif
ifeq ($(KDIR),)
KDIR := /lib/modules/$(shell uname -r)/build
ifeq ($(ARCH),i686)
ifeq ($(wildcard $KDIR/arch/$ARCH),)
ARCH=i386
endif
endif
endif
$(shell rm -rf usbnet.h)
ifneq ($(wildcard $(KDIR)/drivers/usb/net/usbnet.h),)
$(shell ln -s $(KDIR)/drivers/usb/net/usbnet.h usbnet.h)
endif
ifneq ($(wildcard $(KDIR)/drivers/net/usb/usbnet.h),)
$(shell ln -s $(KDIR)/drivers/net/usb/usbnet.h usbnet.h)
endif
default:
ln -sf makefile Makefile
$(MAKE) ARCH=${ARCH} CROSS_COMPILE=${CROSS_COMPILE} -C $(KDIR) M=$(PWD) modules
install: default
mkdir -p $(OUTPUTDIR)
cp -f GobiNet.ko $(OUTPUTDIR)
depmod
modprobe -r GobiNet
modprobe GobiNet
clean:
rm -rf Makefile usbnet.h
rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions Module.* modules.order

1521
quectel_Gobinet/src/QMI.c Normal file

File diff suppressed because it is too large Load Diff

337
quectel_Gobinet/src/QMI.h Normal file
View File

@ -0,0 +1,337 @@
/*===========================================================================
FILE:
QMI.h
DESCRIPTION:
Qualcomm QMI driver header
FUNCTIONS:
Generic QMUX functions
ParseQMUX
FillQMUX
Generic QMI functions
GetTLV
ValidQMIMessage
GetQMIMessageID
Get sizes of buffers needed by QMI requests
QMUXHeaderSize
QMICTLGetClientIDReqSize
QMICTLReleaseClientIDReqSize
QMICTLReadyReqSize
QMIWDSSetEventReportReqSize
QMIWDSGetPKGSRVCStatusReqSize
QMIDMSGetMEIDReqSize
QMICTLSyncReqSize
Fill Buffers with QMI requests
QMICTLGetClientIDReq
QMICTLReleaseClientIDReq
QMICTLReadyReq
QMIWDSSetEventReportReq
QMIWDSGetPKGSRVCStatusReq
QMIDMSGetMEIDReq
QMICTLSetDataFormatReq
QMICTLSyncReq
Parse data from QMI responses
QMICTLGetClientIDResp
QMICTLReleaseClientIDResp
QMIWDSEventResp
QMIDMSGetMEIDResp
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.
===========================================================================*/
#pragma once
/*=========================================================================*/
// Definitions
/*=========================================================================*/
extern int quec_debug;
// DBG macro
#define DBG( format, arg... ) do { \
if (quec_debug == 1)\
{ \
printk( KERN_INFO "GobiNet::%s " format, __FUNCTION__, ## arg ); \
} }while(0)
#if 0
#define VDBG( format, arg... ) do { \
if (debug == 1)\
{ \
printk( KERN_INFO "GobiNet::%s " format, __FUNCTION__, ## arg ); \
} } while(0)
#else
#define VDBG( format, arg... ) do { } while(0)
#endif
#define INFO( format, arg... ) do { \
printk( KERN_INFO "GobiNet::%s " format, __FUNCTION__, ## arg ); \
}while(0)
// QMI Service Types
#define QMICTL 0
#define QMIWDS 1
#define QMIDMS 2
#define QMINAS 3
#define QMIUIM 11
#define QMIWDA 0x1A
#define u8 unsigned char
#define u16 unsigned short
#define u32 unsigned int
#define u64 unsigned long long
#define bool u8
#define true 1
#define false 0
#define ENOMEM 12
#define EFAULT 14
#define EINVAL 22
#ifndef ENOMSG
#define ENOMSG 42
#endif
#define ENODATA 61
#define TLV_TYPE_LINK_PROTO 0x10
/*=========================================================================*/
// Struct sQMUX
//
// Structure that defines a QMUX header
/*=========================================================================*/
typedef struct sQMUX
{
/* T\F, always 1 */
u8 mTF;
/* Size of message */
u16 mLength;
/* Control flag */
u8 mCtrlFlag;
/* Service Type */
u8 mQMIService;
/* Client ID */
u8 mQMIClientID;
}__attribute__((__packed__)) sQMUX;
#if 0
/*=========================================================================*/
// Generic QMUX functions
/*=========================================================================*/
// Remove QMUX headers from a buffer
int ParseQMUX(
u16 * pClientID,
void * pBuffer,
u16 buffSize );
// Fill buffer with QMUX headers
int FillQMUX(
u16 clientID,
void * pBuffer,
u16 buffSize );
/*=========================================================================*/
// Generic QMI functions
/*=========================================================================*/
// Get data buffer of a specified TLV from a QMI message
int GetTLV(
void * pQMIMessage,
u16 messageLen,
u8 type,
void * pOutDataBuf,
u16 bufferLen );
// Check mandatory TLV in a QMI message
int ValidQMIMessage(
void * pQMIMessage,
u16 messageLen );
// Get the message ID of a QMI message
int GetQMIMessageID(
void * pQMIMessage,
u16 messageLen );
/*=========================================================================*/
// Get sizes of buffers needed by QMI requests
/*=========================================================================*/
// Get size of buffer needed for QMUX
u16 QMUXHeaderSize( void );
// Get size of buffer needed for QMUX + QMICTLGetClientIDReq
u16 QMICTLGetClientIDReqSize( void );
// Get size of buffer needed for QMUX + QMICTLReleaseClientIDReq
u16 QMICTLReleaseClientIDReqSize( void );
// Get size of buffer needed for QMUX + QMICTLReadyReq
u16 QMICTLReadyReqSize( void );
// Get size of buffer needed for QMUX + QMIWDSSetEventReportReq
u16 QMIWDSSetEventReportReqSize( void );
// Get size of buffer needed for QMUX + QMIWDSGetPKGSRVCStatusReq
u16 QMIWDSGetPKGSRVCStatusReqSize( void );
u16 QMIWDSSetQMUXBindMuxDataPortSize( void );
// Get size of buffer needed for QMUX + QMIDMSGetMEIDReq
u16 QMIDMSGetMEIDReqSize( void );
// Get size of buffer needed for QMUX + QMIWDASetDataFormatReq
u16 QMIWDASetDataFormatReqSize( int qmap_mode );
// Get size of buffer needed for QMUX + QMICTLSyncReq
u16 QMICTLSyncReqSize( void );
/*=========================================================================*/
// Fill Buffers with QMI requests
/*=========================================================================*/
// Fill buffer with QMI CTL Get Client ID Request
int QMICTLGetClientIDReq(
void * pBuffer,
u16 buffSize,
u8 transactionID,
u8 serviceType );
// Fill buffer with QMI CTL Release Client ID Request
int QMICTLReleaseClientIDReq(
void * pBuffer,
u16 buffSize,
u8 transactionID,
u16 clientID );
// Fill buffer with QMI CTL Get Version Info Request
int QMICTLReadyReq(
void * pBuffer,
u16 buffSize,
u8 transactionID );
// Fill buffer with QMI WDS Set Event Report Request
int QMIWDSSetEventReportReq(
void * pBuffer,
u16 buffSize,
u16 transactionID );
// Fill buffer with QMI WDS Get PKG SRVC Status Request
int QMIWDSGetPKGSRVCStatusReq(
void * pBuffer,
u16 buffSize,
u16 transactionID );
u16 QMIWDSSetQMUXBindMuxDataPortReq(
void * pBuffer,
u16 buffSize,
u8 MuxId,
u16 transactionID );
// Fill buffer with QMI DMS Get Serial Numbers Request
int QMIDMSGetMEIDReq(
void * pBuffer,
u16 buffSize,
u16 transactionID );
// Fill buffer with QMI WDA Set Data Format Request
int QMIWDASetDataFormatReq(
void * pBuffer,
u16 buffSize,
bool bRawIPMode, int qmap_mode, u32 rx_size,
u16 transactionID );
#if 0
int QMIWDASetDataQmapReq(
void * pBuffer,
u16 buffSize,
u16 transactionID );
#endif
int QMICTLSyncReq(
void * pBuffer,
u16 buffSize,
u16 transactionID );
/*=========================================================================*/
// Parse data from QMI responses
/*=========================================================================*/
// Parse the QMI CTL Get Client ID Resp
int QMICTLGetClientIDResp(
void * pBuffer,
u16 buffSize,
u16 * pClientID );
// Verify the QMI CTL Release Client ID Resp is valid
int QMICTLReleaseClientIDResp(
void * pBuffer,
u16 buffSize );
// Parse the QMI WDS Set Event Report Resp/Indication or
// QMI WDS Get PKG SRVC Status Resp/Indication
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 );
// Parse the QMI DMS Get Serial Numbers Resp
int QMIDMSGetMEIDResp(
void * pBuffer,
u16 buffSize,
char * pMEID,
int meidSize );
// Parse the QMI DMS Get Serial Numbers Resp
int QMIWDASetDataFormatResp(
void * pBuffer,
u16 buffSize, bool bRawIPMode, int *qmap_enabled, int *rx_size, int *tx_size);
// Pasre the QMI CTL Sync Response
int QMICTLSyncResp(
void *pBuffer,
u16 buffSize );
#endif

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,368 @@
/*===========================================================================
FILE:
QMIDevice.h
DESCRIPTION:
Functions related to the QMI interface device
FUNCTIONS:
Generic functions
IsDeviceValid
PrintHex
GobiSetDownReason
GobiClearDownReason
GobiTestDownReason
Driver level asynchronous read functions
ResubmitIntURB
ReadCallback
IntCallback
StartRead
KillRead
Internal read/write functions
ReadAsync
UpSem
ReadSync
WriteSyncCallback
WriteSync
Internal memory management functions
GetClientID
ReleaseClientID
FindClientMem
AddToReadMemList
PopFromReadMemList
AddToNotifyList
NotifyAndPopNotifyList
AddToURBList
PopFromURBList
Internal userspace wrapper functions
UserspaceunlockedIOCTL
Userspace wrappers
UserspaceOpen
UserspaceIOCTL
UserspaceClose
UserspaceRead
UserspaceWrite
UserspacePoll
Initializer and destructor
RegisterQMIDevice
DeregisterQMIDevice
Driver level client management
QMIReady
QMIWDSCallback
SetupQMIWDSCallback
QMIDMSGetMEID
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.
===========================================================================*/
//---------------------------------------------------------------------------
// Pragmas
//---------------------------------------------------------------------------
#pragma once
//---------------------------------------------------------------------------
// Include Files
//---------------------------------------------------------------------------
#include "Structs.h"
#include "QMI.h"
/*=========================================================================*/
// Generic functions
/*=========================================================================*/
#ifdef __QUECTEL_INTER__
// Basic test to see if device memory is valid
static bool IsDeviceValid( sGobiUSBNet * pDev );
/*=========================================================================*/
// Driver level asynchronous read functions
/*=========================================================================*/
// Resubmit interrupt URB, re-using same values
static int ResubmitIntURB( struct urb * pIntURB );
// Read callback
// Put the data in storage and notify anyone waiting for data
#if (LINUX_VERSION_CODE > KERNEL_VERSION( 2,6,18 ))
static void ReadCallback( struct urb * pReadURB );
#else
static void ReadCallback(struct urb *pReadURB, struct pt_regs *regs);
#endif
// Inturrupt callback
// Data is available, start a read URB
#if (LINUX_VERSION_CODE > KERNEL_VERSION( 2,6,18 ))
static void IntCallback( struct urb * pIntURB );
#else
static void IntCallback(struct urb *pIntURB, struct pt_regs *regs);
#endif
/*=========================================================================*/
// Internal read/write functions
/*=========================================================================*/
// Start asynchronous read
// Reading client's data store, not device
static int ReadAsync(
sGobiUSBNet * pDev,
u16 clientID,
u16 transactionID,
void (*pCallback)(sGobiUSBNet *, u16, void *),
void * pData );
// Notification function for synchronous read
static void UpSem(
sGobiUSBNet * pDev,
u16 clientID,
void * pData );
// Start synchronous read
// Reading client's data store, not device
static int ReadSync(
sGobiUSBNet * pDev,
void ** ppOutBuffer,
u16 clientID,
u16 transactionID );
// Write callback
#if (LINUX_VERSION_CODE > KERNEL_VERSION( 2,6,18 ))
static void WriteSyncCallback( struct urb * pWriteURB );
#else
static void WriteSyncCallback(struct urb *pWriteURB, struct pt_regs *regs);
#endif
// Start synchronous write
static int WriteSync(
sGobiUSBNet * pDev,
char * pInWriteBuffer,
int size,
u16 clientID );
/*=========================================================================*/
// Internal memory management functions
/*=========================================================================*/
// Create client and allocate memory
static int GetClientID(
sGobiUSBNet * pDev,
u8 serviceType );
// Release client and free memory
static void ReleaseClientID(
sGobiUSBNet * pDev,
u16 clientID );
// Find this client's memory
static sClientMemList * FindClientMem(
sGobiUSBNet * pDev,
u16 clientID );
// Add Data to this client's ReadMem list
static bool AddToReadMemList(
sGobiUSBNet * pDev,
u16 clientID,
u16 transactionID,
void * pData,
u16 dataSize );
// Remove data from this client's ReadMem list if it matches
// the specified transaction ID.
static bool PopFromReadMemList(
sGobiUSBNet * pDev,
u16 clientID,
u16 transactionID,
void ** ppData,
u16 * pDataSize );
// Add Notify entry to this client's notify List
static bool AddToNotifyList(
sGobiUSBNet * pDev,
u16 clientID,
u16 transactionID,
void (* pNotifyFunct)(sGobiUSBNet *, u16, void *),
void * pData );
// Remove first Notify entry from this client's notify list
// and Run function
static bool NotifyAndPopNotifyList(
sGobiUSBNet * pDev,
u16 clientID,
u16 transactionID );
// Add URB to this client's URB list
static bool AddToURBList(
sGobiUSBNet * pDev,
u16 clientID,
struct urb * pURB );
// Remove URB from this client's URB list
static struct urb * PopFromURBList(
sGobiUSBNet * pDev,
u16 clientID );
/*=========================================================================*/
// Internal userspace wrappers
/*=========================================================================*/
// Userspace unlocked ioctl
static long UserspaceunlockedIOCTL(
struct file * pFilp,
unsigned int cmd,
unsigned long arg );
/*=========================================================================*/
// Userspace wrappers
/*=========================================================================*/
// Userspace open
static int UserspaceOpen(
struct inode * pInode,
struct file * pFilp );
#if (LINUX_VERSION_CODE < KERNEL_VERSION( 2,6,36 ))
// Userspace ioctl
static int UserspaceIOCTL(
struct inode * pUnusedInode,
struct file * pFilp,
unsigned int cmd,
unsigned long arg );
#endif
// Userspace close
#define quectel_no_for_each_process
#ifdef quectel_no_for_each_process
static int UserspaceClose(
struct inode * pInode,
struct file * pFilp );
#else
#if (LINUX_VERSION_CODE > KERNEL_VERSION( 2,6,14 ))
static int UserspaceClose(
struct file * pFilp,
fl_owner_t unusedFileTable );
#else
static int UserspaceClose( struct file * pFilp );
#endif
#endif
// Userspace read (synchronous)
static ssize_t UserspaceRead(
struct file * pFilp,
char __user * pBuf,
size_t size,
loff_t * pUnusedFpos );
// Userspace write (synchronous)
static ssize_t UserspaceWrite(
struct file * pFilp,
const char __user * pBuf,
size_t size,
loff_t * pUnusedFpos );
static unsigned int UserspacePoll(
struct file * pFilp,
struct poll_table_struct * pPollTable );
/*=========================================================================*/
// Driver level client management
/*=========================================================================*/
// Check if QMI is ready for use
static bool QMIReady(
sGobiUSBNet * pDev,
u16 timeout );
// QMI WDS callback function
static void QMIWDSCallback(
sGobiUSBNet * pDev,
u16 clientID,
void * pData );
// Fire off reqests and start async read for QMI WDS callback
static int SetupQMIWDSCallback( sGobiUSBNet * pDev );
// Register client, send req and parse MEID response, release client
static int QMIDMSGetMEID( sGobiUSBNet * pDev );
// Register client, send req and parse Data format response, release client
static int QMIWDASetDataFormat( sGobiUSBNet * pDev, int qmap_mode, int *rx_urb_size );
#endif
// Print Hex data, for debug purposes
void QuecPrintHex(
void * pBuffer,
u16 bufSize );
// Sets mDownReason and turns carrier off
void QuecGobiSetDownReason(
sGobiUSBNet * pDev,
u8 reason );
// Clear mDownReason and may turn carrier on
void QuecGobiClearDownReason(
sGobiUSBNet * pDev,
u8 reason );
// Tests mDownReason and returns whether reason is set
bool QuecGobiTestDownReason(
sGobiUSBNet * pDev,
u8 reason );
// Start continuous read "thread"
int QuecStartRead( sGobiUSBNet * pDev );
// Kill continuous read "thread"
void QuecKillRead( sGobiUSBNet * pDev );
/*=========================================================================*/
// Initializer and destructor
/*=========================================================================*/
// QMI Device initialization function
int QuecRegisterQMIDevice( sGobiUSBNet * pDev );
// QMI Device cleanup function
void QuecDeregisterQMIDevice( sGobiUSBNet * pDev );
int QuecQMIWDASetDataFormat( sGobiUSBNet * pDev, int qmap_mode, int *rx_urb_size );
#define PrintHex QuecPrintHex
#define GobiSetDownReason QuecGobiSetDownReason
#define GobiClearDownReason QuecGobiClearDownReason
#define GobiTestDownReason QuecGobiTestDownReason
#define StartRead QuecStartRead
#define KillRead QuecKillRead
#define RegisterQMIDevice QuecRegisterQMIDevice
#define DeregisterQMIDevice QuecDeregisterQMIDevice

View File

@ -0,0 +1,78 @@
Gobi3000 network driver 2011-07-29-1026
This readme covers important information concerning
the Gobi Net driver.
Table of Contents
1. What's new in this release
2. Known issues
3. Known platform issues
-------------------------------------------------------------------------------
1. WHAT'S NEW
This Release (Gobi3000 network driver 2011-07-29-1026)
a. Signal the device to leave low power mode on enumeration
b. Add "txQueueLength" parameter, which will set the Tx Queue Length
c. Send SetControlLineState message during driver/device removal
d. Change to new date-based versioning scheme
Prior Release (Gobi3000 network driver 1.0.60) 06/29/2011
a. Add UserspacePoll() function, to support select()
b. Fix possible deadlock on GobiUSBNetTXTimeout()
c. Fix memory leak on data transmission
Prior Release (Gobi3000 network driver 1.0.50) 05/18/2011
a. Add support for kernels up to 2.6.38
b. Add support for dynamic interface binding
Prior Release (Gobi3000 network driver 1.0.40) 02/28/2011
a. In cases of QMI read errors, discard the error and continue reading.
b. Add "interruptible" parameter, which may be disabled for debugging purposes.
Prior Release (Gobi3000 network driver 1.0.30) 01/05/2011
a. Fix rare kernel PANIC if a process terminates while file handle close
or device removal is in progress.
Prior Release (Gobi3000 network driver 1.0.20) 11/01/2010
a. Fix possible kernel WARNING if device removed before QCWWANDisconnect().
b. Fix multiple memory leaks in error cases.
Prior Release (Gobi3000 network driver 1.0.10) 09/17/2010
a. Initial release
-------------------------------------------------------------------------------
2. KNOWN ISSUES
No known issues.
-------------------------------------------------------------------------------
3. KNOWN PLATFORM ISSUES
a. Enabling autosuspend:
Autosuspend is supported by the Gobi3000 module and its drivers,
but by default it is not enabled by the open source kernel. As such,
the Gobi3000 module will not enter autosuspend unless the
user specifically turns on autosuspend with the command:
echo auto > /sys/bus/usb/devices/.../power/level
b. Ksoftirq using 100% CPU:
There is a known issue with the open source usbnet driver that can
result in infinite software interrupts. The fix for this is to test
(in the usbnet_bh() function) if the usb_device can submit URBs before
attempting to submit the response URB buffers.
c. NetworkManager does not recognize connection after resume:
After resuming from sleep/hibernate, NetworkManager may not recognize new
network connections by the Gobi device. This is a system issue not specific
to the Gobi device, which may result in dhcp not being run and the default
route not being updated. One way to fix this is to simply restart the
NetworkManager service.
-------------------------------------------------------------------------------

View File

@ -0,0 +1,166 @@
Release Notes
[V1.6.3]
Date: 9/26/2021
enhancement:
1. change version to 1.6.3
fix:
[V1.6.2.16]
Date: 9/17/2021
enhancement:
fix:
1. add sdx6x platform support
[V1.6.2.15]
Date: 3/23/2021
enhancement:
fix:
1. add sdx12 platform support
[V1.6.2.14]
Date: 3/18/2021
enhancement:
fix:
1. fix kasam: use-after-free when do modem reboot stress test
2. wait qmi_sync_thread() finish in DeregisterQMIDevice(), usb will disconnect when driver is still in qmi_sync_thread()
[V1.6.2.13]
Date: 12/31/2020
enhancement:
fix:
1. fix quectel-CM open error when driver is still in qmi_sync_thread() but SOC enter sleep.
[V1.6.2.12]
Date: 12/31/2020
enhancement:
fix:
1. for multi-pdn-call, can not ping when usb resume for usb suspend state.
[V1.6.2.11]
Date: 11/7/2020
enhancement:
1. support QUECTEL_QMI_MERGE, for some SOC, control endpoint only support read max 64 bytes QMI.
for QMI that size > 64, we need read serval times, and merge.
fix:
[V1.6.2.10]
Date: 9/15/2020
enhancement:
fix:
1. for X55, fix panic on kernel V2.6 ~ V3.2
[V1.6.2.9]
Date: 7/24/2020
enhancement:
fix:
1. for X55, fix errors on Big Endian SOC.
[V1.6.2.8]
Date: 7/2/2020
enhancement:
1. support QMAPV5, UL AGG (porting from qmi_wwan_q)
fix:
1. fix errors kernel V2.6 .
[V1.6.2.7]
Date: 6/9/2020
enhancement:
fix:
1. when send qmi ctl request, clear qmi ctl response which's TID is same
[V1.6.2.6]
Date: 5/19/2020
enhancement:
1. support bridge mode for multi-pdn-call
fix:
[V1.6.2.5]
Date: 4/26/2020
enhancement:
1. fix netcard name as usbX (from ethX)
fix:
......
[Quectel_WCDMA&LTE_Linux&Android_GobiNet_Driver_V1.5.0]
Date: 2018/04/17
enhancement::
1. support EG20&RG500
2. fix set rx_urb_size as 1520. do not change accroding to MTU
[Quectel_WCDMA&LTE_Linux&Android_GobiNet_Driver_V1.4.3]
Date: 2018/04/16
enhancement::
1. increase QMAP's rx_urb_size to 32KB
[Quectel_WCDMA&LTE_Linux&Android_GobiNet_Driver_V1.4.2]
Date: 2018/04/03
bug fix:
1. fix qmi client can not be released when quectel-CM killed by ¡®kill -9¡¯
[Quectel_WCDMA&LTE_Linux&Android_GobiNet_Driver_V1.4.1]
Date: 2018/02/20
bug fix:
1. fix a compiler error on Kernel lager than 4.11
[Quectel_WCDMA&LTE_Linux&Android_GobiNet_Driver_V1.4.0]
Date: 2018/12/17
bug fix:
1. fix a USB DMA error when built as GobiNet.ko on Kernel lager than 4.15
[Quectel_WCDMA&LTE_Linux&Android_GobiNet_Driver_V1.3.8]
[Quectel_WCDMA&LTE_Linux&Android_GobiNet_Driver_V1.3.7]
Date: 2018/09/25
enhancement:
1. check skb length in tx_fixup functions.
2. when QMAP enabled, set FLAG_RX_ASSEMBLE to advoid 'RX errors' of ifconfig
[Quectel_WCDMA&LTE_Linux&Android_GobiNet_Driver_V1.3.6]
Date: 2018/09/11
enhancement:
1. support EG12 EM12
2. optimization QMAP source code
3. fix compile errors and warnnings on kernel version 4.15
[Quectel_WCDMA&LTE_Linux&Android_GobiNet_Driver_V1.3.5]
Date: 2018/05/12
enhancement:
1. provide two method to enable QMAP function.
1.1 set module parameters 'qmap_mode' to X(1~4) to enable QMAP.
1.2 ifconfig usb0 down, then 'echo X > /sys/class/usbX/qmap_mode' to enable QMAP
for above two method, X(1) used to enable 'IP Aggregation' and X(2~4) to enable 'IP Mux'
2. support bridge mode, also provide two method to enable bridge mode.
2.1 set module parameters 'bridge_mode' to 1 to enable bridge mode.
2.2 'echo 1 > /sys/class/usbX/bridge_mode' to enable bridge mode.
bridge mode setups:
brctl addbr br0; brctl addif br0 eth0; brctl addif usb0; ./quectel-CM; ifconfig br0 up; ifconfig eth0 up
then connect eth0 to PC by ethernet cable. and PC run DHCP tool to obtain network public IP address.
'WCDMA&LTE_QConnectManager_Linux&Android_V1.1.40' and later version is required to use QMAP and bridge mode.
[Quectel_WCDMA&LTE_Linux&Android_GobiNet_Driver_V1.3.4]
Date: 2018/05/07
enhancement:
1. support use 'AT$QCRMCALL=1,1' to setup data call.
when use 'AT$QCRMCALL=1,1', must set module parameters 'qcrmcall_mode' to 1,
and GobiNet Driver will do not tx&rx QMI.
[Quectel_WCDMA&LTE_Linux&Android_GobiNet_Driver_V1.3.3]
Date: 2018/04/04
optimization:
1. optimization QMAP source code
[Quectel_WCDMA&LTE_Linux&Android_GobiNet_Driver_V1.3.2]
Date: 2018/03/23
enhancement:
1. support Qualcomm Mux and Aggregation Protocol (QMAP)
1.1 IP Mux: GobiNet Driver register multiple netcards, one netcards corresponding to one PDP.
and GobiNet Driver will tx/rx multiple IP packets maybe belong to different PDPs in one URB.
1.2 IP Aggregation: GobiNet Driver will rx multiple IP packets in one URB, used to increase throughput theoretically by reducing the number of usb interrupts.
the max rx URB size of MDM9x07 is 4KB, the max rx URB size of MDM9x40&SDX20 is 16KB
[Quectel_WCDMA&LTE_Linux&Android_GobiNet_Driver_V1.3.1]
Date: 2017/11/20
enhancement:
1. support BG96

View File

@ -0,0 +1,529 @@
/*===========================================================================
FILE:
Structs.h
DESCRIPTION:
Declaration of structures used by the Qualcomm Linux USB Network driver
FUNCTIONS:
none
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.
===========================================================================*/
//---------------------------------------------------------------------------
// Pragmas
//---------------------------------------------------------------------------
#pragma once
//---------------------------------------------------------------------------
// Include Files
//---------------------------------------------------------------------------
#include <linux/etherdevice.h>
#include <linux/ethtool.h>
#include <linux/mii.h>
#include <linux/usb.h>
#include <linux/version.h>
#include <linux/cdev.h>
#include <linux/kthread.h>
#include <linux/poll.h>
#include <linux/completion.h>
#include <linux/hrtimer.h>
#include <linux/interrupt.h>
#define QUECTEL_WWAN_QMAP 4 //MAX is 7
#ifdef QUECTEL_WWAN_QMAP
#define QUECTEL_QMAP_MUX_ID 0x81
#endif
//#define QUECTEL_QMI_MERGE
#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
#define QUECTEL_BRIDGE_MODE
#endif
#if (LINUX_VERSION_CODE <= KERNEL_VERSION( 2,6,21 ))
static inline void skb_reset_mac_header(struct sk_buff *skb)
{
skb->mac.raw = skb->data;
}
#endif
#if (LINUX_VERSION_CODE <= KERNEL_VERSION( 2,6,22 ))
#define bool u8
#ifndef URB_FREE_BUFFER
#define URB_FREE_BUFFER_BY_SELF //usb_free_urb will not free, should free by self
#define URB_FREE_BUFFER 0x0100 /* Free transfer buffer with the URB */
#endif
/**
* usb_endpoint_type - get the endpoint's transfer type
* @epd: endpoint to be checked
*
* Returns one of USB_ENDPOINT_XFER_{CONTROL, ISOC, BULK, INT} according
* to @epd's transfer type.
*/
static inline int usb_endpoint_type(const struct usb_endpoint_descriptor *epd)
{
return epd->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK;
}
#endif
#if (LINUX_VERSION_CODE <= KERNEL_VERSION( 2,6,18 ))
/**
* usb_endpoint_dir_in - check if the endpoint has IN direction
* @epd: endpoint to be checked
*
* Returns true if the endpoint is of type IN, otherwise it returns false.
*/
static inline int usb_endpoint_dir_in(const struct usb_endpoint_descriptor *epd)
{
return ((epd->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN);
}
/**
* usb_endpoint_dir_out - check if the endpoint has OUT direction
* @epd: endpoint to be checked
*
* Returns true if the endpoint is of type OUT, otherwise it returns false.
*/
static inline int usb_endpoint_dir_out(
const struct usb_endpoint_descriptor *epd)
{
return ((epd->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_OUT);
}
/**
* usb_endpoint_xfer_int - check if the endpoint has interrupt transfer type
* @epd: endpoint to be checked
*
* Returns true if the endpoint is of type interrupt, otherwise it returns
* false.
*/
static inline int usb_endpoint_xfer_int(
const struct usb_endpoint_descriptor *epd)
{
return ((epd->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) ==
USB_ENDPOINT_XFER_INT);
}
static inline int usb_autopm_set_interface(struct usb_interface *intf)
{ return 0; }
static inline int usb_autopm_get_interface(struct usb_interface *intf)
{ return 0; }
static inline int usb_autopm_get_interface_async(struct usb_interface *intf)
{ return 0; }
static inline void usb_autopm_put_interface(struct usb_interface *intf)
{ }
static inline void usb_autopm_put_interface_async(struct usb_interface *intf)
{ }
static inline void usb_autopm_enable(struct usb_interface *intf)
{ }
static inline void usb_autopm_disable(struct usb_interface *intf)
{ }
static inline void usb_mark_last_busy(struct usb_device *udev)
{ }
#endif
#if (LINUX_VERSION_CODE <= KERNEL_VERSION( 2,6,24 ))
#include "usbnet.h"
#else
#include <linux/usb/usbnet.h>
#endif
#if (LINUX_VERSION_CODE > KERNEL_VERSION( 2,6,25 ))
#include <linux/fdtable.h>
#else
#include <linux/file.h>
#endif
// Used in recursion, defined later below
struct sGobiUSBNet;
#if defined(QUECTEL_WWAN_QMAP)
#define QUECTEL_UL_DATA_AGG 1
#if defined(QUECTEL_UL_DATA_AGG)
struct ul_agg_ctx {
/* QMIWDS_ADMIN_SET_DATA_FORMAT_RESP TLV_0x17 and TLV_0x18 */
uint ul_data_aggregation_max_datagrams; //UplinkDataAggregationMaxDatagramsTlv
uint ul_data_aggregation_max_size; //UplinkDataAggregationMaxSizeTlv
uint dl_minimum_padding;
};
#endif
#endif
/*=========================================================================*/
// Struct sReadMemList
//
// Structure that defines an entry in a Read Memory linked list
/*=========================================================================*/
typedef struct sReadMemList
{
/* Data buffer */
void * mpData;
/* Transaction ID */
u16 mTransactionID;
/* Size of data buffer */
u16 mDataSize;
/* Next entry in linked list */
struct sReadMemList * mpNext;
} sReadMemList;
/*=========================================================================*/
// Struct sNotifyList
//
// Structure that defines an entry in a Notification linked list
/*=========================================================================*/
typedef struct sNotifyList
{
/* Function to be run when data becomes available */
void (* mpNotifyFunct)(struct sGobiUSBNet *, u16, void *);
/* Transaction ID */
u16 mTransactionID;
/* Data to provide as parameter to mpNotifyFunct */
void * mpData;
/* Next entry in linked list */
struct sNotifyList * mpNext;
} sNotifyList;
/*=========================================================================*/
// Struct sURBList
//
// Structure that defines an entry in a URB linked list
/*=========================================================================*/
typedef struct sURBList
{
/* The current URB */
struct urb * mpURB;
/* Next entry in linked list */
struct sURBList * mpNext;
} sURBList;
/*=========================================================================*/
// Struct sClientMemList
//
// Structure that defines an entry in a Client Memory linked list
// Stores data specific to a Service Type and Client ID
/*=========================================================================*/
typedef struct sClientMemList
{
/* Client ID for this Client */
u16 mClientID;
/* Linked list of Read entries */
/* Stores data read from device before sending to client */
sReadMemList * mpList;
/* Linked list of Notification entries */
/* Stores notification functions to be run as data becomes
available or the device is removed */
sNotifyList * mpReadNotifyList;
/* Linked list of URB entries */
/* Stores pointers to outstanding URBs which need canceled
when the client is deregistered or the device is removed */
sURBList * mpURBList;
/* Next entry in linked list */
struct sClientMemList * mpNext;
/* Wait queue object for poll() */
wait_queue_head_t mWaitQueue;
} sClientMemList;
/*=========================================================================*/
// Struct sURBSetupPacket
//
// Structure that defines a USB Setup packet for Control URBs
// Taken from USB CDC specifications
/*=========================================================================*/
typedef struct sURBSetupPacket
{
/* Request type */
u8 mRequestType;
/* Request code */
u8 mRequestCode;
/* Value */
u16 mValue;
/* Index */
u16 mIndex;
/* Length of Control URB */
u16 mLength;
} sURBSetupPacket;
// Common value for sURBSetupPacket.mLength
#define DEFAULT_READ_URB_LENGTH 0x1000
#ifdef QUECTEL_QMI_MERGE
#define MERGE_PACKET_IDENTITY 0x2c7c
#define MERGE_PACKET_VERSION 0x0001
#define MERGE_PACKET_MAX_PAYLOAD_SIZE 56
typedef struct sQMIMsgHeader {
u16 idenity;
u16 version;
u16 cur_len;
u16 total_len;
} sQMIMsgHeader;
typedef struct sQMIMsgPacket {
sQMIMsgHeader header;
u16 len;
char buf[DEFAULT_READ_URB_LENGTH];
} sQMIMsgPacket;
#endif
#ifdef CONFIG_PM
#if (LINUX_VERSION_CODE < KERNEL_VERSION( 2,6,29 ))
/*=========================================================================*/
// Struct sAutoPM
//
// Structure used to manage AutoPM thread which determines whether the
// device is in use or may enter autosuspend. Also submits net
// transmissions asynchronously.
/*=========================================================================*/
typedef struct sAutoPM
{
/* Thread for atomic autopm function */
struct task_struct * mpThread;
/* Signal for completion when it's time for the thread to work */
struct completion mThreadDoWork;
/* Time to exit? */
bool mbExit;
/* List of URB's queued to be sent to the device */
sURBList * mpURBList;
/* URB list lock (for adding and removing elements) */
spinlock_t mURBListLock;
/* Length of the URB list */
atomic_t mURBListLen;
/* Active URB */
struct urb * mpActiveURB;
/* Active URB lock (for adding and removing elements) */
spinlock_t mActiveURBLock;
/* Duplicate pointer to USB device interface */
struct usb_interface * mpIntf;
} sAutoPM;
#endif
#endif /* CONFIG_PM */
/*=========================================================================*/
// Struct sQMIDev
//
// Structure that defines the data for the QMI device
/*=========================================================================*/
typedef struct sQMIDev
{
/* Device number */
dev_t mDevNum;
/* Device class */
struct class * mpDevClass;
/* cdev struct */
struct cdev mCdev;
/* is mCdev initialized? */
bool mbCdevIsInitialized;
/* Pointer to read URB */
struct urb * mpReadURB;
//#define READ_QMI_URB_ERROR
#ifdef READ_QMI_URB_ERROR
struct timer_list mReadUrbTimer;
#endif
#ifdef QUECTEL_QMI_MERGE
sQMIMsgPacket * mpQmiMsgPacket;
#endif
/* Read setup packet */
sURBSetupPacket * mpReadSetupPacket;
/* Read buffer attached to current read URB */
void * mpReadBuffer;
/* Inturrupt URB */
/* Used to asynchronously notify when read data is available */
struct urb * mpIntURB;
/* Buffer used by Inturrupt URB */
void * mpIntBuffer;
/* Pointer to memory linked list for all clients */
sClientMemList * mpClientMemList;
/* Spinlock for client Memory entries */
spinlock_t mClientMemLock;
/* Transaction ID associated with QMICTL "client" */
atomic_t mQMICTLTransactionID;
} sQMIDev;
typedef struct {
u32 qmap_enabled;
u32 dl_data_aggregation_max_datagrams;
u32 dl_data_aggregation_max_size ;
u32 ul_data_aggregation_max_datagrams;
u32 ul_data_aggregation_max_size;
u32 dl_minimum_padding;
} QMAP_SETTING;
/*=========================================================================*/
// Struct sGobiUSBNet
//
// Structure that defines the data associated with the Qualcomm USB device
/*=========================================================================*/
typedef struct sGobiUSBNet
{
atomic_t refcount;
/* Net device structure */
struct usbnet * mpNetDev;
#ifdef QUECTEL_WWAN_QMAP
unsigned link_state;
int qmap_mode;
int qmap_size;
int qmap_version;
struct net_device *mpQmapNetDev[QUECTEL_WWAN_QMAP];
struct tasklet_struct txq;
QMAP_SETTING qmap_settings;
#if defined(QUECTEL_UL_DATA_AGG)
struct ul_agg_ctx agg_ctx;
#endif
#ifdef QUECTEL_BRIDGE_MODE
int m_qmap_bridge_mode[QUECTEL_WWAN_QMAP];
#endif
#endif
#if 1 //def DATA_MODE_RP
bool mbMdm9x07;
bool mbMdm9x06; //for BG96
/* QMI "device" work in IP Mode or ETH Mode */
bool mbRawIPMode;
#ifdef QUECTEL_BRIDGE_MODE
int m_bridge_mode;
uint m_bridge_ipv4;
unsigned char mHostMAC[6];
#endif
int m_qcrmcall_mode;
#endif
struct completion mQMIReadyCompletion;
bool mbQMIReady;
bool mbProbeDone;
bool mbQMISyncIng;
/* Usb device interface */
struct usb_interface * mpIntf;
/* Pointers to usbnet_open and usbnet_stop functions */
int (* mpUSBNetOpen)(struct net_device *);
int (* mpUSBNetStop)(struct net_device *);
/* Reason(s) why interface is down */
/* Used by Gobi*DownReason */
unsigned long mDownReason;
#define NO_NDIS_CONNECTION 0
#define CDC_CONNECTION_SPEED 1
#define DRIVER_SUSPENDED 2
#define NET_IFACE_STOPPED 3
/* QMI "device" status */
bool mbQMIValid;
bool mbDeregisterQMIDevice;
/* QMI "device" memory */
sQMIDev mQMIDev;
/* Device MEID */
char mMEID[14];
struct hrtimer timer;
struct tasklet_struct bh;
unsigned long
pending_num : 8,
pending_size : 16;
struct sk_buff *pending_pool[16];
#ifdef CONFIG_PM
#if (LINUX_VERSION_CODE < KERNEL_VERSION( 2,6,29 ))
/* AutoPM thread */
sAutoPM mAutoPM;
#endif
#endif /* CONFIG_PM */
} sGobiUSBNet;
/*=========================================================================*/
// Struct sQMIFilpStorage
//
// Structure that defines the storage each file handle contains
// Relates the file handle to a client
/*=========================================================================*/
typedef struct sQMIFilpStorage
{
/* Client ID */
u16 mClientID;
/* Device pointer */
sGobiUSBNet * mpDev;
} sQMIFilpStorage;

47
quectel_MHI/Makefile Normal file
View File

@ -0,0 +1,47 @@
#
# Copyright (C) 2015 OpenWrt.org
#
# This is free software, licensed under the GNU General Public License v2.
# See /LICENSE for more information.
#
include $(TOPDIR)/rules.mk
PKG_NAME:=pcie_mhi
PKG_VERSION:=3.2
PKG_RELEASE:=1
include $(INCLUDE_DIR)/kernel.mk
include $(INCLUDE_DIR)/package.mk
define KernelPackage/pcie_mhi
SUBMENU:=WWAN Support
TITLE:=Kernel pcie driver for MHI device
DEPENDS:=+pciids +pciutils +quectel-CM-5G
FILES:=$(PKG_BUILD_DIR)/pcie_mhi.ko
AUTOLOAD:=$(call AutoLoad,90,pcie_mhi)
endef
define KernelPackage/pcie_mhi/description
Kernel module for register a custom pciemhi platform device.
endef
MAKE_OPTS:= \
ARCH="$(LINUX_KARCH)" \
CROSS_COMPILE="$(TARGET_CROSS)" \
CXXFLAGS="$(TARGET_CXXFLAGS)" \
M="$(PKG_BUILD_DIR)" \
$(EXTRA_KCONFIG)
define Build/Prepare
mkdir -p $(PKG_BUILD_DIR)
$(CP) ./src/* $(PKG_BUILD_DIR)/
endef
define Build/Compile
$(MAKE) -C "$(LINUX_DIR)" \
$(MAKE_OPTS) \
modules
endef
$(eval $(call KernelPackage,pcie_mhi))

34
quectel_MHI/src/Makefile Normal file
View File

@ -0,0 +1,34 @@
#ccflags-y += -g
obj-m += pcie_mhi.o
pcie_mhi-objs := core/mhi_init.o core/mhi_main.o core/mhi_pm.o core/mhi_boot.o core/mhi_dtr.o controllers/mhi_qti.o
pcie_mhi-objs += devices/mhi_uci.o
ifeq (1,1)
pcie_mhi-objs += devices/mhi_netdev_quectel.o
else
pcie_mhi-objs += devices/mhi_netdev.o
pcie_mhi-objs += devices/rmnet_handler.o
endif
PWD := $(shell pwd)
ifeq ($(ARCH),)
ARCH := $(shell uname -m)
endif
ifeq ($(CROSS_COMPILE),)
CROSS_COMPILE :=
endif
ifeq ($(KDIR),)
KDIR := /lib/modules/$(shell uname -r)/build
endif
pcie_mhi: clean
$(MAKE) ARCH=${ARCH} CROSS_COMPILE=${CROSS_COMPILE} -C $(KDIR) M=$(PWD) modules
#cp pcie_mhi.ko /tftpboot/
clean:
$(MAKE) ARCH=${ARCH} CROSS_COMPILE=${CROSS_COMPILE} -C $(KDIR) M=$(PWD) clean
find . -name *.o.ur-safe | xargs rm -f
install: pcie_mhi
sudo cp pcie_mhi.ko /lib/modules/${shell uname -r}/kernel/drivers/pci/
sudo depmod

36
quectel_MHI/src/README Normal file
View File

@ -0,0 +1,36 @@
1. porting pcie_mhi driver as next
$ git diff drivers/Makefile
diff --git a/drivers/Makefile b/drivers/Makefile
index 77fbc52..e45837e 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -184,3 +184,4 @@ obj-$(CONFIG_FPGA) += fpga/
obj-$(CONFIG_FSI) += fsi/
obj-$(CONFIG_TEE) += tee/
obj-$(CONFIG_MULTIPLEXER) += mux/
+obj-y += pcie_mhi/
$ tree drivers/pcie_mhi/ -L 1
drivers/pcie_mhi/
controllers
core
devices
Makefile
2. check RG500 attach pcie_mhi driver successful
root@OpenWrt:/# lspci
00:00.0 Class 0604: 17cb:0302
01:00.0 Class ff00: 17cb:0306
root@OpenWrt:~# dmesg | grep mhi
[ 138.483252] mhi_init Quectel_Linux_PCIE_MHI_Driver_V1.3.0.6
[ 138.492350] mhi_pci_probe pci_dev->name = 0000:01:00.0, domain=0, bus=1, slot=0, vendor=17CB, device=0306
3. how to use, see next logs
log/QXDM_OVER_PCIE.txt
log/AT_OVER_PCIE.txt
log/MBIM_OVER_PCIE.txt
log/QMI_OVER_PCIE.txt

View File

@ -0,0 +1,103 @@
Release Notes
[V1.3.4]
Date: 12/8/2022
enhancement:
1. only allow to enable autosuspend when module is in MHI_EE_AMSS
2. show pcie link speed and width when driver probe
3. check pcie link status by read pcie vid and pid when driver probe,
if pcie link is down, return -EIO
4. support RM520 (1eac:1004)
5. support qmap command packet
fix:
1. fix tx queue is wrong stop when do uplink TPUT
2. fix after QFirehose, module fail to bootup at very small probability
3. mhi uci add mutex lock for concurrent reads/writes
[V1.3.3]
Date: 30/6/2022
enhancement:
1. remove one un-necessary kmalloc when do qfirehose
2. support mhi monitor (like usbmon), usage: cat /sys/kernel/debug/mhi_q/0306_00\:01.00/mhimon
3. set ring size of event 0 to 256 (from 1024), required by x6x
4. support PCIE local network card mhi_swip0 (chan 46/47), default disabled
5. porting IPQ5018 mhi rate controll code from spf11.5
6. set pcie rmnet download max qmap packet size to 15KB (same to IPQ MHI Driver)
7. support set different mac address for rmnet net card
8. when mhi netdev fail to malloc, use delay_work instead work
9. optimize code for 'when driver load, modem is still in MHI_EE_PTHRU'
fix:
1. Fix not synchronize access rp/wp when mhi_queue_xxx and mhi_process_xxx_ring run on different CPU
2. set dma mask when driver probe, some SOC like rpi_4 need it
[V1.3.2]
Date: 12/16/2021
enhancement:
1. support Linux Kernel V5.14
2. mhi_netdev_quectel.c do not print log in softirq context
[V1.3.1]
Date: 9/26/2021
enhancement:
fix:
[V1.3.0.19]
Date: 9/18/2021
enhancement:
1. support sdx62 (17cb:0308)
2. support IPQ5018's NSS
3. use 'qsdk/qca/src/data-kernel/drivers/rmnet-nss/rmnet_nss.c' instead myself rmnet_nss.c
and pcie_mhi.ko must load after then rmnet_nss.ko
4. allow bhi irq is not 0 (for ipq5018)
fix:
[V1.3.0.18]
Date: 4/14/2021
enhancement:
1. support mbim multiple call, usage:
# insmod pcie_mhi.ko mhi_mbim_enabeld=1 qmap_mode=4
# quectel-mbim-proxy -d /dev/mhi_MBIM &
# quectel-CM -n X
fix:
[V1.3.0.17]
Date: 3/11/2021
enhancement:
fix:
1. fix CPU loading very high when TPUT test when only one MSI interrupt
2. fix error on latest X24 modem
[V1.3.0.16]
Date: 11/18/2020
enhancement:
fix:
1. add ring size to 32, for in-bound chan, if one ring is full, modem will not generate MSI interrupt for all chan
[V1.3.0.15]
Date: 10/30/2020
enhancement:
1. support multi-modems, named as /dev/mhi_<chan_name>X
fix:
1. fix compile error on kernel v5.8
[V1.3.0.14]
Date: 10/9/2020
enhancement:
1. suppport EM120&EM160
fix:
1. fix compile error on kernel v5.6
2. support runtime suspend
[V1.3.0.13]
Date: 9/7/2020
enhancement:
1. suppport EM120&EM160
fix:
1. fix error on X55 + PCIE2.0(e.g IPQ4019)
2. support runtime suspend
[V1.3.0.12]
Date: 7/7/2020
enhancement:
1. suppport create only none netcard (enabled by marco MHI_NETDEV_ONE_CARD_MODE),
fix:

View File

@ -0,0 +1,13 @@
menu "MHI controllers"
config MHI_QTI
tristate "MHI QTI"
depends on MHI_BUS
help
If you say yes to this option, MHI bus support for QTI modem chipsets
will be enabled. QTI PCIe based modems uses MHI as the communication
protocol. MHI control driver is the bus master for such modems. As the
bus master driver, it oversees power management operations such as
suspend, resume, powering on and off the device.
endmenu

View File

@ -0,0 +1 @@
obj-$(CONFIG_MHI_QTI) += mhi_qti.o mhi_arch_qti.o

View File

@ -0,0 +1,275 @@
// SPDX-License-Identifier: GPL-2.0-only
/* Copyright (c) 2018-2019, The Linux Foundation. All rights reserved.*/
#include <linux/async.h>
#include <linux/debugfs.h>
#include <linux/device.h>
#include <linux/dma-direction.h>
#include <linux/list.h>
#include <linux/of.h>
#include <linux/memblock.h>
#include <linux/module.h>
#include <linux/msm-bus.h>
#include <linux/msm_pcie.h>
#include <linux/pm_runtime.h>
#include <linux/slab.h>
#include "../core/mhi.h"
#include "mhi_qti.h"
struct arch_info {
struct mhi_dev *mhi_dev;
struct msm_bus_scale_pdata *msm_bus_pdata;
u32 bus_client;
struct pci_saved_state *pcie_state;
struct pci_saved_state *ref_pcie_state;
struct dma_iommu_mapping *mapping;
};
struct mhi_bl_info {
struct mhi_device *mhi_device;
async_cookie_t cookie;
void *ipc_log;
};
/* ipc log markings */
#define DLOG "Dev->Host: "
#define HLOG "Host: "
#ifdef CONFIG_MHI_DEBUG
#define MHI_IPC_LOG_PAGES (100)
enum MHI_DEBUG_LEVEL mhi_ipc_log_lvl = MHI_MSG_LVL_VERBOSE;
#else
#define MHI_IPC_LOG_PAGES (10)
enum MHI_DEBUG_LEVEL mhi_ipc_log_lvl = MHI_MSG_LVL_ERROR;
#endif
static int mhi_arch_set_bus_request(struct mhi_controller *mhi_cntrl, int index)
{
struct mhi_dev *mhi_dev = mhi_controller_get_devdata(mhi_cntrl);
struct arch_info *arch_info = mhi_dev->arch_info;
MHI_LOG("Setting bus request to index %d\n", index);
if (arch_info->bus_client)
return msm_bus_scale_client_update_request(
arch_info->bus_client,
index);
/* default return success */
return 0;
}
static void mhi_bl_dl_cb(struct mhi_device *mhi_dev,
struct mhi_result *mhi_result)
{
struct mhi_bl_info *mhi_bl_info = mhi_device_get_devdata(mhi_dev);
char *buf = mhi_result->buf_addr;
/* force a null at last character */
buf[mhi_result->bytes_xferd - 1] = 0;
ipc_log_string(mhi_bl_info->ipc_log, "%s %s", DLOG, buf);
}
static void mhi_bl_dummy_cb(struct mhi_device *mhi_dev,
struct mhi_result *mhi_result)
{
}
static void mhi_bl_remove(struct mhi_device *mhi_dev)
{
struct mhi_bl_info *mhi_bl_info = mhi_device_get_devdata(mhi_dev);
ipc_log_string(mhi_bl_info->ipc_log, HLOG "Received Remove notif.\n");
/* wait for boot monitor to exit */
async_synchronize_cookie(mhi_bl_info->cookie + 1);
}
static void mhi_bl_boot_monitor(void *data, async_cookie_t cookie)
{
struct mhi_bl_info *mhi_bl_info = data;
struct mhi_device *mhi_device = mhi_bl_info->mhi_device;
struct mhi_controller *mhi_cntrl = mhi_device->mhi_cntrl;
/* 15 sec timeout for booting device */
const u32 timeout = msecs_to_jiffies(15000);
/* wait for device to enter boot stage */
wait_event_timeout(mhi_cntrl->state_event, mhi_cntrl->ee == MHI_EE_AMSS
|| mhi_cntrl->ee == MHI_EE_DISABLE_TRANSITION,
timeout);
if (mhi_cntrl->ee == MHI_EE_AMSS) {
ipc_log_string(mhi_bl_info->ipc_log, HLOG
"Device successfully booted to mission mode\n");
mhi_unprepare_from_transfer(mhi_device);
} else {
ipc_log_string(mhi_bl_info->ipc_log, HLOG
"Device failed to boot to mission mode, ee = %s\n",
TO_MHI_EXEC_STR(mhi_cntrl->ee));
}
}
static int mhi_bl_probe(struct mhi_device *mhi_dev,
const struct mhi_device_id *id)
{
char node_name[32];
struct mhi_bl_info *mhi_bl_info;
mhi_bl_info = devm_kzalloc(&mhi_dev->dev, sizeof(*mhi_bl_info),
GFP_KERNEL);
if (!mhi_bl_info)
return -ENOMEM;
snprintf(node_name, sizeof(node_name), "mhi_bl_%04x_%02u.%02u.%02u",
mhi_dev->dev_id, mhi_dev->domain, mhi_dev->bus, mhi_dev->slot);
mhi_bl_info->ipc_log = ipc_log_context_create(MHI_IPC_LOG_PAGES,
node_name, 0);
if (!mhi_bl_info->ipc_log)
return -EINVAL;
mhi_bl_info->mhi_device = mhi_dev;
mhi_device_set_devdata(mhi_dev, mhi_bl_info);
ipc_log_string(mhi_bl_info->ipc_log, HLOG
"Entered SBL, Session ID:0x%x\n",
mhi_dev->mhi_cntrl->session_id);
/* start a thread to monitor entering mission mode */
mhi_bl_info->cookie = async_schedule(mhi_bl_boot_monitor, mhi_bl_info);
return 0;
}
static const struct mhi_device_id mhi_bl_match_table[] = {
{ .chan = "BL" },
{},
};
static struct mhi_driver mhi_bl_driver = {
.id_table = mhi_bl_match_table,
.remove = mhi_bl_remove,
.probe = mhi_bl_probe,
.ul_xfer_cb = mhi_bl_dummy_cb,
.dl_xfer_cb = mhi_bl_dl_cb,
.driver = {
.name = "MHI_BL",
.owner = THIS_MODULE,
},
};
int mhi_arch_pcie_init(struct mhi_controller *mhi_cntrl)
{
struct mhi_dev *mhi_dev = mhi_controller_get_devdata(mhi_cntrl);
struct arch_info *arch_info = mhi_dev->arch_info;
char node[32];
if (!arch_info) {
arch_info = devm_kzalloc(&mhi_dev->pci_dev->dev,
sizeof(*arch_info), GFP_KERNEL);
if (!arch_info)
return -ENOMEM;
mhi_dev->arch_info = arch_info;
snprintf(node, sizeof(node), "mhi_%04x_%02u.%02u.%02u",
mhi_cntrl->dev_id, mhi_cntrl->domain, mhi_cntrl->bus,
mhi_cntrl->slot);
mhi_cntrl->log_buf = ipc_log_context_create(MHI_IPC_LOG_PAGES,
node, 0);
mhi_cntrl->log_lvl = mhi_ipc_log_lvl;
/* save reference state for pcie config space */
arch_info->ref_pcie_state = pci_store_saved_state(
mhi_dev->pci_dev);
mhi_driver_register(&mhi_bl_driver);
}
return mhi_arch_set_bus_request(mhi_cntrl, 1);
}
void mhi_arch_pcie_deinit(struct mhi_controller *mhi_cntrl)
{
mhi_arch_set_bus_request(mhi_cntrl, 0);
}
int mhi_arch_link_off(struct mhi_controller *mhi_cntrl, bool graceful)
{
struct mhi_dev *mhi_dev = mhi_controller_get_devdata(mhi_cntrl);
struct arch_info *arch_info = mhi_dev->arch_info;
struct pci_dev *pci_dev = mhi_dev->pci_dev;
int ret;
MHI_LOG("Entered\n");
if (graceful) {
pci_clear_master(pci_dev);
ret = pci_save_state(mhi_dev->pci_dev);
if (ret) {
MHI_ERR("Failed with pci_save_state, ret:%d\n", ret);
return ret;
}
arch_info->pcie_state = pci_store_saved_state(pci_dev);
pci_disable_device(pci_dev);
}
/*
* We will always attempt to put link into D3hot, however
* link down may have happened due to error fatal, so
* ignoring the return code
*/
pci_set_power_state(pci_dev, PCI_D3hot);
/* release the resources */
mhi_arch_set_bus_request(mhi_cntrl, 0);
MHI_LOG("Exited\n");
return 0;
}
int mhi_arch_link_on(struct mhi_controller *mhi_cntrl)
{
struct mhi_dev *mhi_dev = mhi_controller_get_devdata(mhi_cntrl);
struct arch_info *arch_info = mhi_dev->arch_info;
struct pci_dev *pci_dev = mhi_dev->pci_dev;
int ret;
MHI_LOG("Entered\n");
/* request resources and establish link trainning */
ret = mhi_arch_set_bus_request(mhi_cntrl, 1);
if (ret)
MHI_LOG("Could not set bus frequency, ret:%d\n", ret);
ret = pci_set_power_state(pci_dev, PCI_D0);
if (ret) {
MHI_ERR("Failed to set PCI_D0 state, ret:%d\n", ret);
return ret;
}
ret = pci_enable_device(pci_dev);
if (ret) {
MHI_ERR("Failed to enable device, ret:%d\n", ret);
return ret;
}
ret = pci_load_and_free_saved_state(pci_dev, &arch_info->pcie_state);
if (ret)
MHI_LOG("Failed to load saved cfg state\n");
pci_restore_state(pci_dev);
pci_set_master(pci_dev);
MHI_LOG("Exited\n");
return 0;
}

View File

@ -0,0 +1,715 @@
/* Copyright (c) 2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/debugfs.h>
#include <linux/device.h>
#include <linux/dma-direction.h>
#include <linux/list.h>
#include <linux/of.h>
#include <linux/memblock.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/pm_runtime.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/interrupt.h>
#include <linux/version.h>
#include "../core/mhi.h"
#include "mhi_qcom.h"
#if 1
#ifndef PCI_IRQ_MSI
#define PCI_IRQ_MSI (1 << 1) /* Allow MSI interrupts */
#if (LINUX_VERSION_CODE < KERNEL_VERSION( 3,10,53 ))
int pci_enable_msi_range(struct pci_dev *dev, int minvec, int maxvec)
{
int nvec = maxvec;
int rc;
if (maxvec < minvec)
return -ERANGE;
do {
rc = pci_enable_msi_block(dev, nvec);
if (rc < 0) {
return rc;
} else if (rc > 0) {
if (rc < minvec)
return -ENOSPC;
nvec = rc;
}
} while (rc);
return nvec;
}
#endif
static int pci_alloc_irq_vectors(struct pci_dev *dev, unsigned int min_vecs,
unsigned int max_vecs, unsigned int flags)
{
return pci_enable_msi_range(dev, min_vecs, max_vecs);
}
static void pci_free_irq_vectors(struct pci_dev *dev)
{
pci_disable_msi(dev);
}
static int pci_irq_vector(struct pci_dev *dev, unsigned int nr)
{
return dev->irq + nr;
}
#endif
#endif
static struct pci_device_id mhi_pcie_device_id[] = {
{PCI_DEVICE(MHI_PCIE_VENDOR_ID, 0x0303)}, //SDX20
{PCI_DEVICE(MHI_PCIE_VENDOR_ID, 0x0304)}, //SDX24
{PCI_DEVICE(MHI_PCIE_VENDOR_ID, 0x0305)},
{PCI_DEVICE(MHI_PCIE_VENDOR_ID, 0x0306)}, //SDX55
{PCI_DEVICE(0x2C7C, 0x0512)},
{PCI_DEVICE(MHI_PCIE_VENDOR_ID, MHI_PCIE_DEBUG_ID)},
{0},
};
MODULE_DEVICE_TABLE(pci, mhi_pcie_device_id);
static struct pci_driver mhi_pcie_driver;
void mhi_deinit_pci_dev(struct mhi_controller *mhi_cntrl)
{
struct mhi_dev *mhi_dev = mhi_controller_get_devdata(mhi_cntrl);
struct pci_dev *pci_dev = mhi_dev->pci_dev;
pci_free_irq_vectors(pci_dev);
iounmap(mhi_cntrl->regs);
mhi_cntrl->regs = NULL;
pci_clear_master(pci_dev);
pci_release_region(pci_dev, mhi_dev->resn);
pci_disable_device(pci_dev);
}
static int mhi_init_pci_dev(struct mhi_controller *mhi_cntrl)
{
struct mhi_dev *mhi_dev = mhi_controller_get_devdata(mhi_cntrl);
struct pci_dev *pci_dev = mhi_dev->pci_dev;
int ret;
resource_size_t start, len;
int i;
mhi_dev->resn = MHI_PCI_BAR_NUM;
ret = pci_assign_resource(pci_dev, mhi_dev->resn);
if (ret) {
MHI_ERR("Error assign pci resources, ret:%d\n", ret);
return ret;
}
ret = pci_enable_device(pci_dev);
if (ret) {
MHI_ERR("Error enabling device, ret:%d\n", ret);
goto error_enable_device;
}
ret = pci_request_region(pci_dev, mhi_dev->resn, "mhi");
if (ret) {
MHI_ERR("Error pci_request_region, ret:%d\n", ret);
goto error_request_region;
}
pci_set_master(pci_dev);
start = pci_resource_start(pci_dev, mhi_dev->resn);
len = pci_resource_len(pci_dev, mhi_dev->resn);
mhi_cntrl->regs = ioremap_nocache(start, len);
MHI_LOG("mhi_cntrl->regs = %p\n", mhi_cntrl->regs);
if (!mhi_cntrl->regs) {
MHI_ERR("Error ioremap region\n");
goto error_ioremap;
}
ret = pci_alloc_irq_vectors(pci_dev, 1, mhi_cntrl->msi_required, PCI_IRQ_MSI);
if (IS_ERR_VALUE((ulong)ret) || ret < mhi_cntrl->msi_required) {
if (ret == -ENOSPC) {
/* imx_3.14.52_1.1.0_ga
diff --git a/drivers/pci/host/pcie-designware.c b/drivers/pci/host/pcie-designware.c
index f06e8f0..6a9614f 100644
--- a/drivers/pci/host/pcie-designware.c
+++ b/drivers/pci/host/pcie-designware.c
@@ -376,6 +376,13 @@ static int dw_msi_setup_irq(struct msi_chip *chip, struct pci_dev *pdev,
if (msgvec > 5)
msgvec = 0;
+#if 1 //Add by Quectel 20190419
+ if (msgvec > 0 && pdev->vendor == 0x17cb) {
+ dev_info(&pdev->dev, "%s quectel fixup pos=%d, msg_ctr=%04x, msgvec=%d\n", __func__, desc->msi_attrib.pos, msg_ctr, msgvec);
+ msgvec = 0;
+ }
+#endif
+
irq = assign_irq((1 << msgvec), desc, &pos);
if (irq < 0)
return irq;
*/
}
//imx_4.1.15_2.0.0_ga & DELL_OPTIPLEX_7010 only alloc one msi interrupt for one pcie device
if (ret != 1) {
MHI_ERR("Failed to enable MSI, ret=%d, msi_required=%d\n", ret, mhi_cntrl->msi_required);
goto error_req_msi;
}
}
mhi_cntrl->msi_allocated = ret;
MHI_LOG("msi_required = %d, msi_allocated = %d, msi_irq = %u\n", mhi_cntrl->msi_required, mhi_cntrl->msi_allocated, pci_dev->irq);
for (i = 0; i < mhi_cntrl->msi_allocated; i++) {
mhi_cntrl->irq[i] = pci_irq_vector(pci_dev, i);
if (mhi_cntrl->irq[i] < 0) {
ret = mhi_cntrl->irq[i];
goto error_get_irq_vec;
}
}
#if 0
/* configure runtime pm */
pm_runtime_set_autosuspend_delay(&pci_dev->dev, MHI_RPM_SUSPEND_TMR_MS);
pm_runtime_dont_use_autosuspend(&pci_dev->dev);
pm_suspend_ignore_children(&pci_dev->dev, true);
/*
* pci framework will increment usage count (twice) before
* calling local device driver probe function.
* 1st pci.c pci_pm_init() calls pm_runtime_forbid
* 2nd pci-driver.c local_pci_probe calls pm_runtime_get_sync
* Framework expect pci device driver to call
* pm_runtime_put_noidle to decrement usage count after
* successful probe and and call pm_runtime_allow to enable
* runtime suspend.
*/
pm_runtime_mark_last_busy(&pci_dev->dev);
pm_runtime_put_noidle(&pci_dev->dev);
#endif
return 0;
error_get_irq_vec:
pci_free_irq_vectors(pci_dev);
error_req_msi:
iounmap(mhi_cntrl->regs);
error_ioremap:
pci_clear_master(pci_dev);
error_request_region:
pci_disable_device(pci_dev);
error_enable_device:
pci_release_region(pci_dev, mhi_dev->resn);
return ret;
}
#ifdef CONFIG_PM
static int mhi_runtime_idle(struct device *dev)
{
struct mhi_controller *mhi_cntrl = dev_get_drvdata(dev);
MHI_LOG("Entered returning -EBUSY\n");
/*
* RPM framework during runtime resume always calls
* rpm_idle to see if device ready to suspend.
* If dev.power usage_count count is 0, rpm fw will call
* rpm_idle cb to see if device is ready to suspend.
* if cb return 0, or cb not defined the framework will
* assume device driver is ready to suspend;
* therefore, fw will schedule runtime suspend.
* In MHI power management, MHI host shall go to
* runtime suspend only after entering MHI State M2, even if
* usage count is 0. Return -EBUSY to disable automatic suspend.
*/
return -EBUSY;
}
static int mhi_runtime_suspend(struct device *dev)
{
int ret = 0;
struct mhi_controller *mhi_cntrl = dev_get_drvdata(dev);
MHI_LOG("Enter\n");
mutex_lock(&mhi_cntrl->pm_mutex);
ret = mhi_pm_suspend(mhi_cntrl);
if (ret) {
MHI_LOG("Abort due to ret:%d\n", ret);
goto exit_runtime_suspend;
}
ret = mhi_arch_link_off(mhi_cntrl, true);
if (ret)
MHI_ERR("Failed to Turn off link ret:%d\n", ret);
exit_runtime_suspend:
mutex_unlock(&mhi_cntrl->pm_mutex);
MHI_LOG("Exited with ret:%d\n", ret);
return ret;
}
static int mhi_runtime_resume(struct device *dev)
{
int ret = 0;
struct mhi_controller *mhi_cntrl = dev_get_drvdata(dev);
struct mhi_dev *mhi_dev = mhi_controller_get_devdata(mhi_cntrl);
MHI_LOG("Enter\n");
mutex_lock(&mhi_cntrl->pm_mutex);
if (!mhi_dev->powered_on) {
MHI_LOG("Not fully powered, return success\n");
mutex_unlock(&mhi_cntrl->pm_mutex);
return 0;
}
/* turn on link */
ret = mhi_arch_link_on(mhi_cntrl);
if (ret)
goto rpm_resume_exit;
/* enter M0 state */
ret = mhi_pm_resume(mhi_cntrl);
rpm_resume_exit:
mutex_unlock(&mhi_cntrl->pm_mutex);
MHI_LOG("Exited with :%d\n", ret);
return ret;
}
static int mhi_system_resume(struct device *dev)
{
int ret = 0;
struct mhi_controller *mhi_cntrl = dev_get_drvdata(dev);
ret = mhi_runtime_resume(dev);
if (ret) {
MHI_ERR("Failed to resume link\n");
} else {
pm_runtime_set_active(dev);
pm_runtime_enable(dev);
}
return ret;
}
int mhi_system_suspend(struct device *dev)
{
struct mhi_controller *mhi_cntrl = dev_get_drvdata(dev);
MHI_LOG("Entered\n");
/* if rpm status still active then force suspend */
if (!pm_runtime_status_suspended(dev))
return mhi_runtime_suspend(dev);
pm_runtime_set_suspended(dev);
pm_runtime_disable(dev);
MHI_LOG("Exit\n");
return 0;
}
#endif
/* checks if link is down */
static int mhi_link_status(struct mhi_controller *mhi_cntrl, void *priv)
{
struct mhi_dev *mhi_dev = priv;
u16 dev_id;
int ret;
/* try reading device id, if dev id don't match, link is down */
ret = pci_read_config_word(mhi_dev->pci_dev, PCI_DEVICE_ID, &dev_id);
return (ret || dev_id != mhi_cntrl->dev_id) ? -EIO : 0;
}
static int mhi_runtime_get(struct mhi_controller *mhi_cntrl, void *priv)
{
struct mhi_dev *mhi_dev = priv;
struct device *dev = &mhi_dev->pci_dev->dev;
return pm_runtime_get(dev);
}
static void mhi_runtime_put(struct mhi_controller *mhi_cntrl, void *priv)
{
struct mhi_dev *mhi_dev = priv;
struct device *dev = &mhi_dev->pci_dev->dev;
pm_runtime_put_noidle(dev);
}
static void mhi_status_cb(struct mhi_controller *mhi_cntrl,
void *priv,
enum MHI_CB reason)
{
struct mhi_dev *mhi_dev = priv;
struct device *dev = &mhi_dev->pci_dev->dev;
if (reason == MHI_CB_IDLE) {
MHI_LOG("Schedule runtime suspend 1\n");
pm_runtime_mark_last_busy(dev);
pm_request_autosuspend(dev);
}
}
int mhi_debugfs_trigger_m0(void *data, u64 val)
{
struct mhi_controller *mhi_cntrl = data;
struct mhi_dev *mhi_dev = mhi_controller_get_devdata(mhi_cntrl);
MHI_LOG("Trigger M3 Exit\n");
pm_runtime_get(&mhi_dev->pci_dev->dev);
pm_runtime_put(&mhi_dev->pci_dev->dev);
return 0;
}
int mhi_debugfs_trigger_m3(void *data, u64 val)
{
struct mhi_controller *mhi_cntrl = data;
struct mhi_dev *mhi_dev = mhi_controller_get_devdata(mhi_cntrl);
MHI_LOG("Trigger M3 Entry\n");
pm_runtime_mark_last_busy(&mhi_dev->pci_dev->dev);
pm_request_autosuspend(&mhi_dev->pci_dev->dev);
return 0;
}
DEFINE_SIMPLE_ATTRIBUTE(debugfs_trigger_m0_fops, NULL,
mhi_debugfs_trigger_m0, "%llu\n");
DEFINE_SIMPLE_ATTRIBUTE(debugfs_trigger_m3_fops, NULL,
mhi_debugfs_trigger_m3, "%llu\n");
static int mhi_init_debugfs_trigger_go(void *data, u64 val)
{
struct mhi_controller *mhi_cntrl = data;
MHI_LOG("Trigger power up sequence\n");
mhi_async_power_up(mhi_cntrl);
return 0;
}
DEFINE_SIMPLE_ATTRIBUTE(mhi_init_debugfs_trigger_go_fops, NULL,
mhi_init_debugfs_trigger_go, "%llu\n");
int mhi_init_debugfs_debug_show(struct seq_file *m, void *d)
{
seq_puts(m, "Enable debug mode to debug external soc\n");
seq_puts(m,
"Usage: echo 'devid,timeout,domain,smmu_cfg' > debug_mode\n");
seq_puts(m, "No spaces between parameters\n");
seq_puts(m, "\t1. devid : 0 or pci device id to register\n");
seq_puts(m, "\t2. timeout: mhi cmd/state transition timeout\n");
seq_puts(m, "\t3. domain: Rootcomplex\n");
seq_puts(m, "\t4. smmu_cfg: smmu configuration mask:\n");
seq_puts(m, "\t\t- BIT0: ATTACH\n");
seq_puts(m, "\t\t- BIT1: S1 BYPASS\n");
seq_puts(m, "\t\t-BIT2: FAST_MAP\n");
seq_puts(m, "\t\t-BIT3: ATOMIC\n");
seq_puts(m, "\t\t-BIT4: FORCE_COHERENT\n");
seq_puts(m, "\t\t-BIT5: GEOMETRY\n");
seq_puts(m, "\tAll timeout are in ms, enter 0 to keep default\n");
seq_puts(m, "Examples inputs: '0x307,10000'\n");
seq_puts(m, "\techo '0,10000,1'\n");
seq_puts(m, "\techo '0x307,10000,0,0x3d'\n");
seq_puts(m, "firmware image name will be changed to debug.mbn\n");
return 0;
}
static int mhi_init_debugfs_debug_open(struct inode *node, struct file *file)
{
return single_open(file, mhi_init_debugfs_debug_show, NULL);
}
static ssize_t mhi_init_debugfs_debug_write(struct file *fp,
const char __user *ubuf,
size_t count,
loff_t *pos)
{
char *buf = kmalloc(count + 1, GFP_KERNEL);
/* #,devid,timeout,domain,smmu-cfg */
int args[5] = {0};
static char const *dbg_fw = "debug.mbn";
int ret;
struct mhi_controller *mhi_cntrl = fp->f_inode->i_private;
struct mhi_dev *mhi_dev = mhi_controller_get_devdata(mhi_cntrl);
struct pci_device_id *id;
if (!buf)
return -ENOMEM;
ret = copy_from_user(buf, ubuf, count);
if (ret)
goto error_read;
buf[count] = 0;
get_options(buf, ARRAY_SIZE(args), args);
kfree(buf);
/* override default parameters */
mhi_cntrl->fw_image = dbg_fw;
mhi_cntrl->edl_image = dbg_fw;
if (args[0] >= 2 && args[2])
mhi_cntrl->timeout_ms = args[2];
if (args[0] >= 3 && args[3])
mhi_cntrl->domain = args[3];
if (args[0] >= 4 && args[4])
mhi_dev->smmu_cfg = args[4];
/* If it's a new device id register it */
if (args[0] && args[1]) {
/* find the debug_id and overwrite it */
for (id = mhi_pcie_device_id; id->vendor; id++)
if (id->device == MHI_PCIE_DEBUG_ID) {
id->device = args[1];
pci_unregister_driver(&mhi_pcie_driver);
ret = pci_register_driver(&mhi_pcie_driver);
}
}
mhi_dev->debug_mode = true;
debugfs_create_file("go", 0444, mhi_cntrl->parent, mhi_cntrl,
&mhi_init_debugfs_trigger_go_fops);
pr_info(
"%s: ret:%d pcidev:0x%x smm_cfg:%u timeout:%u\n",
__func__, ret, args[1], mhi_dev->smmu_cfg,
mhi_cntrl->timeout_ms);
return count;
error_read:
kfree(buf);
return ret;
}
static const struct file_operations debugfs_debug_ops = {
.open = mhi_init_debugfs_debug_open,
.release = single_release,
.read = seq_read,
.write = mhi_init_debugfs_debug_write,
};
static struct mhi_controller * mhi_platform_probe(struct pci_dev *pci_dev)
{
struct mhi_controller *mhi_cntrl;
struct mhi_dev *mhi_dev;
u64 addr_win[2];
int ret;
mhi_cntrl = mhi_alloc_controller(sizeof(*mhi_dev));
if (!mhi_cntrl) {
pr_err("mhi_alloc_controller fail\n");
return NULL;
}
mhi_dev = mhi_controller_get_devdata(mhi_cntrl);
mhi_cntrl->dev_id = pci_dev->device;
mhi_cntrl->domain = pci_domain_nr(pci_dev->bus);
mhi_cntrl->bus = pci_dev->bus->number;
mhi_cntrl->slot = PCI_SLOT(pci_dev->devfn);
mhi_dev->smmu_cfg = 0;
#if 0 //def CONFIG_HAVE_MEMBLOCK
addr_win[0] = memblock_start_of_DRAM();
addr_win[1] = memblock_end_of_DRAM();
#else
#define MHI_MEM_BASE_DEFAULT 0x000000000
#define MHI_MEM_SIZE_DEFAULT 0x2000000000
addr_win[0] = MHI_MEM_BASE_DEFAULT;
addr_win[1] = MHI_MEM_SIZE_DEFAULT;
if (sizeof(dma_addr_t) == 4) {
addr_win[1] = 0xFFFFFFFF;
}
#endif
mhi_cntrl->iova_start = addr_win[0];
mhi_cntrl->iova_stop = addr_win[1];
mhi_dev->pci_dev = pci_dev;
mhi_cntrl->pci_dev = pci_dev;
/* setup power management apis */
mhi_cntrl->status_cb = mhi_status_cb;
mhi_cntrl->runtime_get = mhi_runtime_get;
mhi_cntrl->runtime_put = mhi_runtime_put;
mhi_cntrl->link_status = mhi_link_status;
ret = mhi_arch_platform_init(mhi_dev);
if (ret)
goto error_probe;
ret = mhi_register_mhi_controller(mhi_cntrl);
if (ret)
goto error_register;
if (mhi_cntrl->parent)
debugfs_create_file("debug_mode", 0444, mhi_cntrl->parent,
mhi_cntrl, &debugfs_debug_ops);
return mhi_cntrl;
error_register:
mhi_arch_platform_deinit(mhi_dev);
error_probe:
mhi_free_controller(mhi_cntrl);
return NULL;
}
int mhi_pci_probe(struct pci_dev *pci_dev,
const struct pci_device_id *device_id)
{
struct mhi_controller *mhi_cntrl = NULL;
u32 domain = pci_domain_nr(pci_dev->bus);
u32 bus = pci_dev->bus->number;
u32 slot = PCI_SLOT(pci_dev->devfn);
struct mhi_dev *mhi_dev;
int ret;
pr_info("%s pci_dev->name = %s, domain=%d, bus=%d, slot=%d, vendor=%04X, device=%04X\n",
__func__, dev_name(&pci_dev->dev), domain, bus, slot, pci_dev->vendor, pci_dev->device);
mhi_cntrl = mhi_platform_probe(pci_dev);
if (!mhi_cntrl) {
pr_err("mhi_platform_probe fail\n");
return -EPROBE_DEFER;
}
mhi_cntrl->dev_id = pci_dev->device;
mhi_dev = mhi_controller_get_devdata(mhi_cntrl);
mhi_dev->pci_dev = pci_dev;
mhi_dev->powered_on = true;
ret = mhi_arch_pcie_init(mhi_cntrl);
if (ret) {
MHI_ERR("Error mhi_arch_pcie_init, ret:%d\n", ret);
return ret;
}
ret = mhi_arch_iommu_init(mhi_cntrl);
if (ret) {
MHI_ERR("Error mhi_arch_iommu_init, ret:%d\n", ret);
goto error_iommu_init;
}
ret = mhi_init_pci_dev(mhi_cntrl);
if (ret) {
MHI_ERR("Error mhi_init_pci_dev, ret:%d\n", ret);
goto error_init_pci;
}
/* start power up sequence if not in debug mode */
if (!mhi_dev->debug_mode) {
ret = mhi_async_power_up(mhi_cntrl);
if (ret) {
MHI_ERR("Error mhi_async_power_up, ret:%d\n", ret);
goto error_power_up;
}
}
#if 0
pm_runtime_mark_last_busy(&pci_dev->dev);
pm_runtime_allow(&pci_dev->dev);
pm_runtime_disable(&pci_dev->dev);
#endif
if (mhi_cntrl->dentry) {
debugfs_create_file("m0", 0444, mhi_cntrl->dentry, mhi_cntrl,
&debugfs_trigger_m0_fops);
debugfs_create_file("m3", 0444, mhi_cntrl->dentry, mhi_cntrl,
&debugfs_trigger_m3_fops);
}
dev_set_drvdata(&pci_dev->dev, mhi_cntrl);
MHI_LOG("Return successful\n");
return 0;
error_power_up:
mhi_deinit_pci_dev(mhi_cntrl);
error_init_pci:
mhi_arch_iommu_deinit(mhi_cntrl);
error_iommu_init:
mhi_arch_pcie_deinit(mhi_cntrl);
return ret;
}
static void mhi_pci_remove(struct pci_dev *pci_dev)
{
struct mhi_controller *mhi_cntrl = (struct mhi_controller *)dev_get_drvdata(&pci_dev->dev);
if (mhi_cntrl && mhi_cntrl->pci_dev == pci_dev) {
struct mhi_dev *mhi_dev = mhi_controller_get_devdata(mhi_cntrl);
MHI_LOG("%s\n", dev_name(&pci_dev->dev));
if (!mhi_dev->debug_mode) {
mhi_power_down(mhi_cntrl, 1);
}
mhi_deinit_pci_dev(mhi_cntrl);
mhi_arch_iommu_deinit(mhi_cntrl);
mhi_arch_pcie_deinit(mhi_cntrl);
mhi_unregister_mhi_controller(mhi_cntrl);
}
}
static const struct dev_pm_ops pm_ops = {
SET_RUNTIME_PM_OPS(mhi_runtime_suspend,
mhi_runtime_resume,
mhi_runtime_idle)
SET_SYSTEM_SLEEP_PM_OPS(mhi_system_suspend, mhi_system_resume)
};
static struct pci_driver mhi_pcie_driver = {
.name = "mhi",
.id_table = mhi_pcie_device_id,
.probe = mhi_pci_probe,
.remove = mhi_pci_remove,
.driver = {
.pm = &pm_ops
}
};
int __init mhi_controller_qcom_init(void)
{
return pci_register_driver(&mhi_pcie_driver);
};
void mhi_controller_qcom_exit(void)
{
pr_info("%s enter\n", __func__);
pci_unregister_driver(&mhi_pcie_driver);
pr_info("%s exit\n", __func__);
}

View File

@ -0,0 +1,92 @@
/* Copyright (c) 2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef _MHI_QCOM_
#define _MHI_QCOM_
/* iova cfg bitmask */
#define MHI_SMMU_ATTACH BIT(0)
#define MHI_SMMU_S1_BYPASS BIT(1)
#define MHI_SMMU_FAST BIT(2)
#define MHI_SMMU_ATOMIC BIT(3)
#define MHI_SMMU_FORCE_COHERENT BIT(4)
#define MHI_PCIE_VENDOR_ID (0x17cb)
#define MHI_PCIE_DEBUG_ID (0xffff)
#define MHI_RPM_SUSPEND_TMR_MS (3000)
#define MHI_PCI_BAR_NUM (0)
struct mhi_dev {
struct pci_dev *pci_dev;
u32 smmu_cfg;
int resn;
void *arch_info;
bool powered_on;
bool debug_mode;
};
void mhi_deinit_pci_dev(struct mhi_controller *mhi_cntrl);
int mhi_pci_probe(struct pci_dev *pci_dev,
const struct pci_device_id *device_id);
#if (LINUX_VERSION_CODE < KERNEL_VERSION( 3,10,65 ))
static inline int dma_set_mask_and_coherent(struct device *dev, u64 mask)
{
int rc = dma_set_mask(dev, mask);
if (rc == 0)
dma_set_coherent_mask(dev, mask);
return rc;
}
#endif
static inline int mhi_arch_iommu_init(struct mhi_controller *mhi_cntrl)
{
struct mhi_dev *mhi_dev = mhi_controller_get_devdata(mhi_cntrl);
mhi_cntrl->dev = &mhi_dev->pci_dev->dev;
return dma_set_mask_and_coherent(mhi_cntrl->dev, DMA_BIT_MASK(64));
}
static inline void mhi_arch_iommu_deinit(struct mhi_controller *mhi_cntrl)
{
}
static inline int mhi_arch_pcie_init(struct mhi_controller *mhi_cntrl)
{
return 0;
}
static inline void mhi_arch_pcie_deinit(struct mhi_controller *mhi_cntrl)
{
}
static inline int mhi_arch_platform_init(struct mhi_dev *mhi_dev)
{
return 0;
}
static inline void mhi_arch_platform_deinit(struct mhi_dev *mhi_dev)
{
}
static inline int mhi_arch_link_off(struct mhi_controller *mhi_cntrl,
bool graceful)
{
return 0;
}
static inline int mhi_arch_link_on(struct mhi_controller *mhi_cntrl)
{
return 0;
}
#endif /* _MHI_QCOM_ */

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,44 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/* Copyright (c) 2018-2019, The Linux Foundation. All rights reserved.*/
#ifndef _MHI_QTI_
#define _MHI_QTI_
/* iova cfg bitmask */
#define MHI_SMMU_ATTACH BIT(0)
#define MHI_SMMU_S1_BYPASS BIT(1)
#define MHI_SMMU_FAST BIT(2)
#define MHI_SMMU_ATOMIC BIT(3)
#define MHI_SMMU_FORCE_COHERENT BIT(4)
#define MHI_PCIE_VENDOR_ID (0x17cb)
#define MHI_PCIE_DEBUG_ID (0xffff)
/* runtime suspend timer */
#define MHI_RPM_SUSPEND_TMR_MS (2000)
#define MHI_PCI_BAR_NUM (0)
struct mhi_dev {
struct pci_dev *pci_dev;
u32 smmu_cfg;
int resn;
void *arch_info;
bool powered_on;
dma_addr_t iova_start;
dma_addr_t iova_stop;
bool lpm_disabled;
};
void mhi_deinit_pci_dev(struct mhi_controller *mhi_cntrl);
int mhi_pci_probe(struct pci_dev *pci_dev,
const struct pci_device_id *device_id);
void mhi_pci_device_removed(struct pci_dev *pci_dev);
int mhi_arch_pcie_init(struct mhi_controller *mhi_cntrl);
void mhi_arch_pcie_deinit(struct mhi_controller *mhi_cntrl);
int mhi_arch_iommu_init(struct mhi_controller *mhi_cntrl);
void mhi_arch_iommu_deinit(struct mhi_controller *mhi_cntrl);
int mhi_arch_link_off(struct mhi_controller *mhi_cntrl, bool graceful);
int mhi_arch_link_on(struct mhi_controller *mhi_cntrl);
#endif /* _MHI_QTI_ */

View File

@ -0,0 +1 @@
obj-$(CONFIG_MHI_BUS) +=mhi_init.o mhi_main.o mhi_pm.o mhi_boot.o mhi_dtr.o

890
quectel_MHI/src/core/mhi.h Normal file
View File

@ -0,0 +1,890 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/* Copyright (c) 2018-2019, The Linux Foundation. All rights reserved. */
#ifndef _MHI_H_
#define _MHI_H_
#define PCIE_MHI_DRIVER_VERSION "V1.3.4"
#define ENABLE_MHI_MON
//#define ENABLE_IP_SW0
#include <linux/miscdevice.h>
typedef enum
{
MHI_CLIENT_LOOPBACK_OUT = 0,
MHI_CLIENT_LOOPBACK_IN = 1,
MHI_CLIENT_SAHARA_OUT = 2,
MHI_CLIENT_SAHARA_IN = 3,
MHI_CLIENT_DIAG_OUT = 4,
MHI_CLIENT_DIAG_IN = 5,
MHI_CLIENT_SSR_OUT = 6,
MHI_CLIENT_SSR_IN = 7,
MHI_CLIENT_QDSS_OUT = 8,
MHI_CLIENT_QDSS_IN = 9,
MHI_CLIENT_EFS_OUT = 10,
MHI_CLIENT_EFS_IN = 11,
MHI_CLIENT_MBIM_OUT = 12,
MHI_CLIENT_MBIM_IN = 13,
MHI_CLIENT_QMI_OUT = 14,
MHI_CLIENT_QMI_IN = 15,
MHI_CLIENT_QMI_2_OUT = 16,
MHI_CLIENT_QMI_2_IN = 17,
MHI_CLIENT_IP_CTRL_1_OUT = 18,
MHI_CLIENT_IP_CTRL_1_IN = 19,
MHI_CLIENT_IPCR_OUT = 20,
MHI_CLIENT_IPCR_IN = 21,
MHI_CLIENT_TEST_FW_OUT = 22,
MHI_CLIENT_TEST_FW_IN = 23,
MHI_CLIENT_RESERVED_0 = 24,
MHI_CLIENT_BOOT_LOG_IN = 25,
MHI_CLIENT_DCI_OUT = 26,
MHI_CLIENT_DCI_IN = 27,
MHI_CLIENT_QBI_OUT = 28,
MHI_CLIENT_QBI_IN = 29,
MHI_CLIENT_RESERVED_1_LOWER = 30,
MHI_CLIENT_RESERVED_1_UPPER = 31,
MHI_CLIENT_DUN_OUT = 32,
MHI_CLIENT_DUN_IN = 33,
MHI_CLIENT_EDL_OUT = 34,
MHI_CLIENT_EDL_IN = 35,
MHI_CLIENT_ADB_FB_OUT = 36,
MHI_CLIENT_ADB_FB_IN = 37,
MHI_CLIENT_RESERVED_2_LOWER = 38,
MHI_CLIENT_RESERVED_2_UPPER = 41,
MHI_CLIENT_CSVT_OUT = 42,
MHI_CLIENT_CSVT_IN = 43,
MHI_CLIENT_SMCT_OUT = 44,
MHI_CLIENT_SMCT_IN = 45,
MHI_CLIENT_IP_SW_0_OUT = 46,
MHI_CLIENT_IP_SW_0_IN = 47,
MHI_CLIENT_IP_SW_1_OUT = 48,
MHI_CLIENT_IP_SW_1_IN = 49,
MHI_CLIENT_RESERVED_3_LOWER = 50,
MHI_CLIENT_RESERVED_3_UPPER = 59,
MHI_CLIENT_TEST_0_OUT = 60,
MHI_CLIENT_TEST_0_IN = 61,
MHI_CLIENT_TEST_1_OUT = 62,
MHI_CLIENT_TEST_1_IN = 63,
MHI_CLIENT_TEST_2_OUT = 64,
MHI_CLIENT_TEST_2_IN = 65,
MHI_CLIENT_TEST_3_OUT = 66,
MHI_CLIENT_TEST_3_IN = 67,
MHI_CLIENT_RESERVED_4_LOWER = 68,
MHI_CLIENT_RESERVED_4_UPPER = 91,
MHI_CLIENT_OEM_0_OUT = 92,
MHI_CLIENT_OEM_0_IN = 93,
MHI_CLIENT_OEM_1_OUT = 94,
MHI_CLIENT_OEM_1_IN = 95,
MHI_CLIENT_OEM_2_OUT = 96,
MHI_CLIENT_OEM_2_IN = 97,
MHI_CLIENT_OEM_3_OUT = 98,
MHI_CLIENT_OEM_3_IN = 99,
MHI_CLIENT_IP_HW_0_OUT = 100,
MHI_CLIENT_IP_HW_0_IN = 101,
MHI_CLIENT_ADPL = 102,
MHI_CLIENT_RESERVED_5_LOWER = 103,
MHI_CLIENT_RESERVED_5_UPPER = 127,
MHI_MAX_CHANNELS = 128
}MHI_CLIENT_CHANNEL_TYPE;
/* Event Ring Index */
typedef enum
{
SW_EVT_RING = 0,
PRIMARY_EVENT_RING = SW_EVT_RING,
#ifdef ENABLE_IP_SW0
SW_0_OUT_EVT_RING,
SW_0_IN_EVT_RING,
#endif
IPA_OUT_EVENT_RING,
IPA_IN_EVENT_RING,
ADPL_EVT_RING,
MAX_EVT_RING_IDX
}MHI_EVT_RING_IDX;
#define MHI_VERSION 0x01000000
#define MHIREGLEN_VALUE 0x100 /* **** WRONG VALUE *** */
#define MHI_MSI_INDEX 1
#define MAX_NUM_MHI_DEVICES 1
#define NUM_MHI_XFER_RINGS 128
#define NUM_MHI_EVT_RINGS MAX_EVT_RING_IDX
#define NUM_MHI_HW_EVT_RINGS 3
#define NUM_MHI_XFER_RING_ELEMENTS 16
#define NUM_MHI_EVT_RING_ELEMENTS (NUM_MHI_IPA_IN_RING_ELEMENTS*2) //must *2, event ring full will make x55 dump
#define NUM_MHI_IPA_IN_RING_ELEMENTS 512
#define NUM_MHI_IPA_OUT_RING_ELEMENTS 512 //donot use ul agg, so increase
#define NUM_MHI_DIAG_IN_RING_ELEMENTS 128
#define NUM_MHI_SW_IP_RING_ELEMENTS 512
/*
* for if set Interrupt moderation time as 1ms,
and transfer more than NUM_MHI_CHAN_RING_ELEMENTS data are sent to the modem in 1ms.
e.g. firehose upgrade.
modem will not trigger irq for these transfer.
*/
#define NUM_MHI_CHAN_RING_ELEMENTS 32 //8
#define MHI_EVT_CMD_QUEUE_SIZE 160
#define MHI_EVT_STATE_QUEUE_SIZE 128
#define MHI_EVT_XFER_QUEUE_SIZE 1024
#define CHAN_INBOUND(_x) ((_x)%2)
#define CHAN_SBL(_x) (((_x) == MHI_CLIENT_SAHARA_OUT) || \
((_x) == MHI_CLIENT_SAHARA_IN) || \
((_x) == MHI_CLIENT_BOOT_LOG_IN))
#define CHAN_EDL(_x) (((_x) == MHI_CLIENT_EDL_OUT) || \
((_x) == MHI_CLIENT_EDL_IN))
struct mhi_chan;
struct mhi_event;
struct mhi_ctxt;
struct mhi_cmd;
struct image_info;
struct bhi_vec_entry;
struct mhi_timesync;
struct mhi_buf_info;
/**
* enum MHI_CB - MHI callback
* @MHI_CB_IDLE: MHI entered idle state
* @MHI_CB_PENDING_DATA: New data available for client to process
* @MHI_CB_LPM_ENTER: MHI host entered low power mode
* @MHI_CB_LPM_EXIT: MHI host about to exit low power mode
* @MHI_CB_EE_RDDM: MHI device entered RDDM execution enviornment
* @MHI_CB_EE_MISSION_MODE: MHI device entered Mission Mode exec env
* @MHI_CB_SYS_ERROR: MHI device enter error state (may recover)
* @MHI_CB_FATAL_ERROR: MHI device entered fatal error
*/
enum MHI_CB {
MHI_CB_IDLE,
MHI_CB_PENDING_DATA,
MHI_CB_LPM_ENTER,
MHI_CB_LPM_EXIT,
MHI_CB_EE_RDDM,
MHI_CB_EE_MISSION_MODE,
MHI_CB_SYS_ERROR,
MHI_CB_FATAL_ERROR,
};
/**
* enum MHI_DEBUG_LEVL - various debugging level
*/
enum MHI_DEBUG_LEVEL {
MHI_MSG_LVL_VERBOSE,
MHI_MSG_LVL_INFO,
MHI_MSG_LVL_ERROR,
MHI_MSG_LVL_CRITICAL,
MHI_MSG_LVL_MASK_ALL,
};
/*
GSI_XFER_FLAG_BEI: Block event interrupt
1: Event generated by this ring element must not assert an interrupt to the host
0: Event generated by this ring element must assert an interrupt to the host
GSI_XFER_FLAG_EOT: Interrupt on end of transfer
1: If an EOT condition is encountered when processing this ring element, an event is generated by the device with its completion code set to EOT.
0: If an EOT condition is encountered for this ring element, a completion event is not be generated by the device, unless IEOB is 1
GSI_XFER_FLAG_EOB: Interrupt on end of block
1: Device notifies host after processing this ring element by sending a completion event
0: Completion event is not required after processing this ring element
GSI_XFER_FLAG_CHAIN: Chain bit that identifies the ring elements in a TD
*/
/**
* enum MHI_FLAGS - Transfer flags
* @MHI_EOB: End of buffer for bulk transfer
* @MHI_EOT: End of transfer
* @MHI_CHAIN: Linked transfer
*/
enum MHI_FLAGS {
MHI_EOB,
MHI_EOT,
MHI_CHAIN,
};
/**
* enum mhi_device_type - Device types
* @MHI_XFER_TYPE: Handles data transfer
* @MHI_TIMESYNC_TYPE: Use for timesync feature
* @MHI_CONTROLLER_TYPE: Control device
*/
enum mhi_device_type {
MHI_XFER_TYPE,
MHI_TIMESYNC_TYPE,
MHI_CONTROLLER_TYPE,
};
/**
* enum mhi_ee - device current execution enviornment
* @MHI_EE_PBL - device in PBL
* @MHI_EE_SBL - device in SBL
* @MHI_EE_AMSS - device in mission mode (firmware fully loaded)
* @MHI_EE_RDDM - device in ram dump collection mode
* @MHI_EE_WFW - device in WLAN firmware mode
* @MHI_EE_PTHRU - device in PBL but configured in pass thru mode
* @MHI_EE_EDL - device in emergency download mode
*/
enum mhi_ee {
MHI_EE_PBL = 0x0,
MHI_EE_SBL = 0x1,
MHI_EE_AMSS = 0x2,
MHI_EE_RDDM = 0x3,
MHI_EE_WFW = 0x4,
MHI_EE_PTHRU = 0x5,
MHI_EE_EDL = 0x6,
MHI_EE_FP = 0x7, /* FlashProg, Flash Programmer Environment */
MHI_EE_MAX_SUPPORTED = MHI_EE_FP,
MHI_EE_DISABLE_TRANSITION, /* local EE, not related to mhi spec */
MHI_EE_MAX,
};
/**
* enum mhi_dev_state - device current MHI state
*/
enum mhi_dev_state {
MHI_STATE_RESET = 0x0,
MHI_STATE_READY = 0x1,
MHI_STATE_M0 = 0x2,
MHI_STATE_M1 = 0x3,
MHI_STATE_M2 = 0x4,
MHI_STATE_M3 = 0x5,
MHI_STATE_BHI = 0x7,
MHI_STATE_SYS_ERR = 0xFF,
MHI_STATE_MAX,
};
extern const char * const mhi_ee_str[MHI_EE_MAX];
#define TO_MHI_EXEC_STR(ee) (((ee) >= MHI_EE_MAX) ? \
"INVALID_EE" : mhi_ee_str[ee])
/**
* struct image_info - firmware and rddm table table
* @mhi_buf - Contain device firmware and rddm table
* @entries - # of entries in table
*/
struct image_info {
struct mhi_buf *mhi_buf;
struct bhi_vec_entry *bhi_vec;
u32 entries;
};
/**
* struct mhi_controller - Master controller structure for external modem
* @dev: Device associated with this controller
* @of_node: DT that has MHI configuration information
* @regs: Points to base of MHI MMIO register space
* @bhi: Points to base of MHI BHI register space
* @bhie: Points to base of MHI BHIe register space
* @wake_db: MHI WAKE doorbell register address
* @dev_id: PCIe device id of the external device
* @domain: PCIe domain the device connected to
* @bus: PCIe bus the device assigned to
* @slot: PCIe slot for the modem
* @iova_start: IOMMU starting address for data
* @iova_stop: IOMMU stop address for data
* @fw_image: Firmware image name for normal booting
* @edl_image: Firmware image name for emergency download mode
* @fbc_download: MHI host needs to do complete image transfer
* @rddm_size: RAM dump size that host should allocate for debugging purpose
* @sbl_size: SBL image size
* @seg_len: BHIe vector size
* @fbc_image: Points to firmware image buffer
* @rddm_image: Points to RAM dump buffer
* @max_chan: Maximum number of channels controller support
* @mhi_chan: Points to channel configuration table
* @lpm_chans: List of channels that require LPM notifications
* @total_ev_rings: Total # of event rings allocated
* @hw_ev_rings: Number of hardware event rings
* @sw_ev_rings: Number of software event rings
* @msi_required: Number of msi required to operate
* @msi_allocated: Number of msi allocated by bus master
* @irq: base irq # to request
* @mhi_event: MHI event ring configurations table
* @mhi_cmd: MHI command ring configurations table
* @mhi_ctxt: MHI device context, shared memory between host and device
* @timeout_ms: Timeout in ms for state transitions
* @pm_state: Power management state
* @ee: MHI device execution environment
* @dev_state: MHI STATE
* @status_cb: CB function to notify various power states to but master
* @link_status: Query link status in case of abnormal value read from device
* @runtime_get: Async runtime resume function
* @runtimet_put: Release votes
* @time_get: Return host time in us
* @lpm_disable: Request controller to disable link level low power modes
* @lpm_enable: Controller may enable link level low power modes again
* @priv_data: Points to bus master's private data
*/
struct mhi_controller {
struct list_head node;
struct mhi_device *mhi_dev;
/* device node for iommu ops */
struct device *dev;
struct device_node *of_node;
/* mmio base */
phys_addr_t base_addr;
void __iomem *regs;
void __iomem *bhi;
void __iomem *bhie;
void __iomem *wake_db;
/* device topology */
u32 vendor;
u32 dev_id;
u32 domain;
u32 bus;
u32 slot;
u32 cntrl_idx;
struct device *cntrl_dev;
/* addressing window */
dma_addr_t iova_start;
dma_addr_t iova_stop;
/* fw images */
const char *fw_image;
const char *edl_image;
/* mhi host manages downloading entire fbc images */
bool fbc_download;
size_t rddm_size;
size_t sbl_size;
size_t seg_len;
u32 session_id;
u32 sequence_id;
struct image_info *fbc_image;
struct image_info *rddm_image;
/* physical channel config data */
u32 max_chan;
struct mhi_chan *mhi_chan;
struct list_head lpm_chans; /* these chan require lpm notification */
/* physical event config data */
u32 total_ev_rings;
u32 hw_ev_rings;
u32 sw_ev_rings;
u32 msi_required;
u32 msi_allocated;
u32 msi_irq_base;
int *irq; /* interrupt table */
struct mhi_event *mhi_event;
/* cmd rings */
struct mhi_cmd *mhi_cmd;
/* mhi context (shared with device) */
struct mhi_ctxt *mhi_ctxt;
u32 timeout_ms;
/* caller should grab pm_mutex for suspend/resume operations */
struct mutex pm_mutex;
bool pre_init;
rwlock_t pm_lock;
u32 pm_state;
enum mhi_ee ee;
enum mhi_dev_state dev_state;
bool wake_set;
atomic_t dev_wake;
atomic_t alloc_size;
atomic_t pending_pkts;
struct list_head transition_list;
spinlock_t transition_lock;
spinlock_t wlock;
/* debug counters */
u32 M0, M2, M3;
/* worker for different state transitions */
struct work_struct st_worker;
struct work_struct fw_worker;
struct work_struct syserr_worker;
struct delayed_work ready_worker;
wait_queue_head_t state_event;
/* shadow functions */
void (*status_cb)(struct mhi_controller *mhi_cntrl, void *priv,
enum MHI_CB reason);
int (*link_status)(struct mhi_controller *mhi_cntrl, void *priv);
void (*wake_get)(struct mhi_controller *mhi_cntrl, bool override);
void (*wake_put)(struct mhi_controller *mhi_cntrl, bool override);
int (*runtime_get)(struct mhi_controller *mhi_cntrl, void *priv);
void (*runtime_put)(struct mhi_controller *mhi_cntrl, void *priv);
void (*runtime_mark_last_busy)(struct mhi_controller *mhi_cntrl, void *priv);
u64 (*time_get)(struct mhi_controller *mhi_cntrl, void *priv);
int (*lpm_disable)(struct mhi_controller *mhi_cntrl, void *priv);
int (*lpm_enable)(struct mhi_controller *mhi_cntrl, void *priv);
int (*map_single)(struct mhi_controller *mhi_cntrl,
struct mhi_buf_info *buf);
void (*unmap_single)(struct mhi_controller *mhi_cntrl,
struct mhi_buf_info *buf);
/* channel to control DTR messaging */
struct mhi_device *dtr_dev;
/* bounce buffer settings */
bool bounce_buf;
size_t buffer_len;
/* supports time sync feature */
struct mhi_timesync *mhi_tsync;
struct mhi_device *tsync_dev;
/* kernel log level */
enum MHI_DEBUG_LEVEL klog_lvl;
int klog_slient;
/* private log level controller driver to set */
enum MHI_DEBUG_LEVEL log_lvl;
/* controller specific data */
void *priv_data;
void *log_buf;
struct dentry *dentry;
struct dentry *parent;
struct miscdevice miscdev;
#ifdef ENABLE_MHI_MON
spinlock_t lock;
/* Ref */
int nreaders; /* Under mon_lock AND mbus->lock */
struct list_head r_list; /* Chain of readers (usually one) */
struct kref ref; /* Under mon_lock */
/* Stats */
unsigned int cnt_events;
unsigned int cnt_text_lost;
#endif
};
#ifdef ENABLE_MHI_MON
struct mhi_tre;
struct mon_reader {
struct list_head r_link;
struct mhi_controller *m_bus;
void *r_data; /* Use container_of instead? */
void (*rnf_submit)(void *data, u32 chan, dma_addr_t wp, struct mhi_tre *mhi_tre, void *buf, size_t len);
void (*rnf_receive)(void *data, u32 chan, dma_addr_t wp, struct mhi_tre *mhi_tre, void *buf, size_t len);
void (*rnf_complete)(void *data, u32 chan, dma_addr_t wp, struct mhi_tre *mhi_tre);
};
#endif
/**
* struct mhi_device - mhi device structure associated bind to channel
* @dev: Device associated with the channels
* @mtu: Maximum # of bytes controller support
* @ul_chan_id: MHI channel id for UL transfer
* @dl_chan_id: MHI channel id for DL transfer
* @tiocm: Device current terminal settings
* @priv: Driver private data
*/
struct mhi_device {
struct device dev;
u32 vendor;
u32 dev_id;
u32 domain;
u32 bus;
u32 slot;
size_t mtu;
int ul_chan_id;
int dl_chan_id;
int ul_event_id;
int dl_event_id;
u32 tiocm;
const struct mhi_device_id *id;
const char *chan_name;
struct mhi_controller *mhi_cntrl;
struct mhi_chan *ul_chan;
struct mhi_chan *dl_chan;
atomic_t dev_wake;
enum mhi_device_type dev_type;
void *priv_data;
int (*ul_xfer)(struct mhi_device *mhi_dev, struct mhi_chan *mhi_chan,
void *buf, size_t len, enum MHI_FLAGS flags);
int (*dl_xfer)(struct mhi_device *mhi_dev, struct mhi_chan *mhi_chan,
void *buf, size_t size, enum MHI_FLAGS flags);
void (*status_cb)(struct mhi_device *mhi_dev, enum MHI_CB reason);
};
/**
* struct mhi_result - Completed buffer information
* @buf_addr: Address of data buffer
* @dir: Channel direction
* @bytes_xfer: # of bytes transferred
* @transaction_status: Status of last trasnferred
*/
struct mhi_result {
void *buf_addr;
enum dma_data_direction dir;
size_t bytes_xferd;
int transaction_status;
};
/**
* struct mhi_buf - Describes the buffer
* @page: buffer as a page
* @buf: cpu address for the buffer
* @phys_addr: physical address of the buffer
* @dma_addr: iommu address for the buffer
* @skb: skb of ip packet
* @len: # of bytes
* @name: Buffer label, for offload channel configurations name must be:
* ECA - Event context array data
* CCA - Channel context array data
*/
struct mhi_buf {
struct list_head node;
struct page *page;
void *buf;
phys_addr_t phys_addr;
dma_addr_t dma_addr;
struct sk_buff *skb;
size_t len;
const char *name; /* ECA, CCA */
};
/**
* struct mhi_driver - mhi driver information
* @id_table: NULL terminated channel ID names
* @ul_xfer_cb: UL data transfer callback
* @dl_xfer_cb: DL data transfer callback
* @status_cb: Asynchronous status callback
*/
struct mhi_driver {
const struct mhi_device_id *id_table;
int (*probe)(struct mhi_device *mhi_dev,
const struct mhi_device_id *id);
void (*remove)(struct mhi_device *mhi_dev);
void (*ul_xfer_cb)(struct mhi_device *mhi_dev, struct mhi_result *res);
void (*dl_xfer_cb)(struct mhi_device *mhi_dev, struct mhi_result *res);
void (*status_cb)(struct mhi_device *mhi_dev, enum MHI_CB mhi_cb);
struct device_driver driver;
};
#define to_mhi_driver(drv) container_of(drv, struct mhi_driver, driver)
#define to_mhi_device(dev) container_of(dev, struct mhi_device, dev)
static inline void mhi_device_set_devdata(struct mhi_device *mhi_dev,
void *priv)
{
mhi_dev->priv_data = priv;
}
static inline void *mhi_device_get_devdata(struct mhi_device *mhi_dev)
{
return mhi_dev->priv_data;
}
/**
* mhi_queue_transfer - Queue a buffer to hardware
* All transfers are asyncronous transfers
* @mhi_dev: Device associated with the channels
* @dir: Data direction
* @buf: Data buffer (skb for hardware channels)
* @len: Size in bytes
* @mflags: Interrupt flags for the device
*/
static inline int mhi_queue_transfer(struct mhi_device *mhi_dev,
enum dma_data_direction dir,
void *buf,
size_t len,
enum MHI_FLAGS mflags)
{
if (dir == DMA_TO_DEVICE)
return mhi_dev->ul_xfer(mhi_dev, mhi_dev->ul_chan, buf, len,
mflags);
else
return mhi_dev->dl_xfer(mhi_dev, mhi_dev->dl_chan, buf, len,
mflags);
}
static inline void *mhi_controller_get_devdata(struct mhi_controller *mhi_cntrl)
{
return mhi_cntrl->priv_data;
}
static inline void mhi_free_controller(struct mhi_controller *mhi_cntrl)
{
kfree(mhi_cntrl);
}
/**
* mhi_driver_register - Register driver with MHI framework
* @mhi_drv: mhi_driver structure
*/
int mhi_driver_register(struct mhi_driver *mhi_drv);
/**
* mhi_driver_unregister - Unregister a driver for mhi_devices
* @mhi_drv: mhi_driver structure
*/
void mhi_driver_unregister(struct mhi_driver *mhi_drv);
/**
* mhi_device_configure - configure ECA or CCA context
* For offload channels that client manage, call this
* function to configure channel context or event context
* array associated with the channel
* @mhi_div: Device associated with the channels
* @dir: Direction of the channel
* @mhi_buf: Configuration data
* @elements: # of configuration elements
*/
int mhi_device_configure(struct mhi_device *mhi_div,
enum dma_data_direction dir,
struct mhi_buf *mhi_buf,
int elements);
/**
* mhi_device_get - disable all low power modes
* Only disables lpm, does not immediately exit low power mode
* if controller already in a low power mode
* @mhi_dev: Device associated with the channels
*/
void mhi_device_get(struct mhi_device *mhi_dev);
/**
* mhi_device_get_sync - disable all low power modes
* Synchronously disable all low power, exit low power mode if
* controller already in a low power state
* @mhi_dev: Device associated with the channels
*/
int mhi_device_get_sync(struct mhi_device *mhi_dev);
/**
* mhi_device_put - re-enable low power modes
* @mhi_dev: Device associated with the channels
*/
void mhi_device_put(struct mhi_device *mhi_dev);
/**
* mhi_prepare_for_transfer - setup channel for data transfer
* Moves both UL and DL channel from RESET to START state
* @mhi_dev: Device associated with the channels
*/
int mhi_prepare_for_transfer(struct mhi_device *mhi_dev);
/**
* mhi_unprepare_from_transfer -unprepare the channels
* Moves both UL and DL channels to RESET state
* @mhi_dev: Device associated with the channels
*/
void mhi_unprepare_from_transfer(struct mhi_device *mhi_dev);
/**
* mhi_get_no_free_descriptors - Get transfer ring length
* Get # of TD available to queue buffers
* @mhi_dev: Device associated with the channels
* @dir: Direction of the channel
*/
int mhi_get_no_free_descriptors(struct mhi_device *mhi_dev,
enum dma_data_direction dir);
/**
* mhi_poll - poll for any available data to consume
* This is only applicable for DL direction
* @mhi_dev: Device associated with the channels
* @budget: In descriptors to service before returning
*/
int mhi_poll(struct mhi_device *mhi_dev, u32 budget);
/**
* mhi_ioctl - user space IOCTL support for MHI channels
* Native support for setting TIOCM
* @mhi_dev: Device associated with the channels
* @cmd: IOCTL cmd
* @arg: Optional parameter, iotcl cmd specific
*/
long mhi_ioctl(struct mhi_device *mhi_dev, unsigned int cmd, unsigned long arg);
/**
* mhi_alloc_controller - Allocate mhi_controller structure
* Allocate controller structure and additional data for controller
* private data. You may get the private data pointer by calling
* mhi_controller_get_devdata
* @size: # of additional bytes to allocate
*/
struct mhi_controller *mhi_alloc_controller(size_t size);
/**
* of_register_mhi_controller - Register MHI controller
* Registers MHI controller with MHI bus framework. DT must be supported
* @mhi_cntrl: MHI controller to register
*/
int of_register_mhi_controller(struct mhi_controller *mhi_cntrl);
void mhi_unregister_mhi_controller(struct mhi_controller *mhi_cntrl);
/**
* mhi_bdf_to_controller - Look up a registered controller
* Search for controller based on device identification
* @domain: RC domain of the device
* @bus: Bus device connected to
* @slot: Slot device assigned to
* @dev_id: Device Identification
*/
struct mhi_controller *mhi_bdf_to_controller(u32 domain, u32 bus, u32 slot,
u32 dev_id);
/**
* mhi_prepare_for_power_up - Do pre-initialization before power up
* This is optional, call this before power up if controller do not
* want bus framework to automatically free any allocated memory during shutdown
* process.
* @mhi_cntrl: MHI controller
*/
int mhi_prepare_for_power_up(struct mhi_controller *mhi_cntrl);
/**
* mhi_async_power_up - Starts MHI power up sequence
* @mhi_cntrl: MHI controller
*/
int mhi_async_power_up(struct mhi_controller *mhi_cntrl);
int mhi_sync_power_up(struct mhi_controller *mhi_cntrl);
/**
* mhi_power_down - Start MHI power down sequence
* @mhi_cntrl: MHI controller
* @graceful: link is still accessible, do a graceful shutdown process otherwise
* we will shutdown host w/o putting device into RESET state
*/
void mhi_power_down(struct mhi_controller *mhi_cntrl, bool graceful);
/**
* mhi_unprepare_after_powre_down - free any allocated memory for power up
* @mhi_cntrl: MHI controller
*/
void mhi_unprepare_after_power_down(struct mhi_controller *mhi_cntrl);
/**
* mhi_pm_suspend - Move MHI into a suspended state
* Transition to MHI state M3 state from M0||M1||M2 state
* @mhi_cntrl: MHI controller
*/
int mhi_pm_suspend(struct mhi_controller *mhi_cntrl);
/**
* mhi_pm_resume - Resume MHI from suspended state
* Transition to MHI state M0 state from M3 state
* @mhi_cntrl: MHI controller
*/
int mhi_pm_resume(struct mhi_controller *mhi_cntrl);
/**
* mhi_download_rddm_img - Download ramdump image from device for
* debugging purpose.
* @mhi_cntrl: MHI controller
* @in_panic: If we trying to capture image while in kernel panic
*/
int mhi_download_rddm_img(struct mhi_controller *mhi_cntrl, bool in_panic);
/**
* mhi_force_rddm_mode - Force external device into rddm mode
* to collect device ramdump. This is useful if host driver assert
* and we need to see device state as well.
* @mhi_cntrl: MHI controller
*/
int mhi_force_rddm_mode(struct mhi_controller *mhi_cntrl);
/**
* mhi_get_remote_time_sync - Get external soc time relative to local soc time
* using MMIO method.
* @mhi_dev: Device associated with the channels
* @t_host: Pointer to output local soc time
* @t_dev: Pointer to output remote soc time
*/
int mhi_get_remote_time_sync(struct mhi_device *mhi_dev,
u64 *t_host,
u64 *t_dev);
/**
* mhi_get_mhi_state - Return MHI state of device
* @mhi_cntrl: MHI controller
*/
enum mhi_dev_state mhi_get_mhi_state(struct mhi_controller *mhi_cntrl);
/**
* mhi_set_mhi_state - Set device state
* @mhi_cntrl: MHI controller
* @state: state to set
*/
void mhi_set_mhi_state(struct mhi_controller *mhi_cntrl,
enum mhi_dev_state state);
/**
* mhi_is_active - helper function to determine if MHI in active state
* @mhi_dev: client device
*/
static inline bool mhi_is_active(struct mhi_device *mhi_dev)
{
struct mhi_controller *mhi_cntrl = mhi_dev->mhi_cntrl;
return (mhi_cntrl->dev_state >= MHI_STATE_M0 &&
mhi_cntrl->dev_state <= MHI_STATE_M3);
}
/**
* mhi_debug_reg_dump - dump MHI registers for debug purpose
* @mhi_cntrl: MHI controller
*/
void mhi_debug_reg_dump(struct mhi_controller *mhi_cntrl);
#ifdef CONFIG_MHI_DEBUG
#define MHI_VERB(fmt, ...) do { \
if (mhi_cntrl->klog_lvl <= MHI_MSG_LVL_VERBOSE) \
pr_debug("[D][mhi%d][%s] " fmt, mhi_cntrl->cntrl_idx, __func__, ##__VA_ARGS__);\
} while (0)
#else
#define MHI_VERB(fmt, ...)
#endif
#define MHI_LOG(fmt, ...) do { \
if (mhi_cntrl->klog_lvl <= MHI_MSG_LVL_INFO) \
pr_info("[I][mhi%d][%s] " fmt, mhi_cntrl->cntrl_idx, __func__, ##__VA_ARGS__);\
else if (!mhi_cntrl->klog_slient) \
printk(KERN_DEBUG "[I][mhi%d][%s] " fmt, mhi_cntrl->cntrl_idx, __func__, ##__VA_ARGS__);\
} while (0)
#define MHI_ERR(fmt, ...) do { \
if (mhi_cntrl->klog_lvl <= MHI_MSG_LVL_ERROR) \
pr_err("[E][mhi%d][%s] " fmt, mhi_cntrl->cntrl_idx, __func__, ##__VA_ARGS__); \
} while (0)
#define MHI_CRITICAL(fmt, ...) do { \
if (mhi_cntrl->klog_lvl <= MHI_MSG_LVL_CRITICAL) \
pr_alert("[C][mhi%d][%s] " fmt, mhi_cntrl->cntrl_idx, __func__, ##__VA_ARGS__); \
} while (0)
int mhi_register_mhi_controller(struct mhi_controller *mhi_cntrl);
void mhi_unregister_mhi_controller(struct mhi_controller *mhi_cntrl);
#ifndef MHI_NAME_SIZE
#define MHI_NAME_SIZE 32
/**
* * struct mhi_device_id - MHI device identification
* * @chan: MHI channel name
* * @driver_data: driver data;
* */
struct mhi_device_id {
const char chan[MHI_NAME_SIZE];
unsigned long driver_data;
};
#endif
#endif /* _MHI_H_ */

View File

@ -0,0 +1,846 @@
// SPDX-License-Identifier: GPL-2.0-only
/* Copyright (c) 2018-2019, The Linux Foundation. All rights reserved. */
#include <linux/debugfs.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/dma-direction.h>
#include <linux/dma-mapping.h>
#include <linux/firmware.h>
#include <linux/interrupt.h>
#include <linux/list.h>
#include <linux/of.h>
#include <linux/module.h>
#include <linux/random.h>
#include <linux/slab.h>
#include <linux/wait.h>
#include <linux/sched.h>
#include <linux/uaccess.h>
#include "mhi.h"
#include "mhi_internal.h"
/* Software defines */
/* BHI Version */
#define BHI_MAJOR_VERSION 0x1
#define BHI_MINOR_VERSION 0x1
#define MSMHWID_NUMDWORDS 6 /* Number of dwords that make the MSMHWID */
#define OEMPKHASH_NUMDWORDS 48 /* Number of dwords that make the OEM PK HASH */
#define IsPBLExecEnv(ExecEnv) ((ExecEnv == MHI_EE_PBL) || (ExecEnv == MHI_EE_EDL) )
typedef u32 ULONG;
typedef struct _bhi_info_type
{
ULONG bhi_ver_minor;
ULONG bhi_ver_major;
ULONG bhi_image_address_low;
ULONG bhi_image_address_high;
ULONG bhi_image_size;
ULONG bhi_rsvd1;
ULONG bhi_imgtxdb;
ULONG bhi_rsvd2;
ULONG bhi_msivec;
ULONG bhi_rsvd3;
ULONG bhi_ee;
ULONG bhi_status;
ULONG bhi_errorcode;
ULONG bhi_errdbg1;
ULONG bhi_errdbg2;
ULONG bhi_errdbg3;
ULONG bhi_sernum;
ULONG bhi_sblantirollbackver;
ULONG bhi_numsegs;
ULONG bhi_msmhwid[6];
ULONG bhi_oempkhash[48];
ULONG bhi_rsvd5;
}BHI_INFO_TYPE, *PBHI_INFO_TYPE;
static void PrintBhiInfo(struct mhi_controller *mhi_cntrl, BHI_INFO_TYPE *bhi_info)
{
ULONG index;
char str[128];
MHI_LOG("BHI Device Info...\n");
MHI_LOG("BHI Version = { Major = 0x%X Minor = 0x%X}\n", bhi_info->bhi_ver_major, bhi_info->bhi_ver_minor);
MHI_LOG("BHI Execution Environment = 0x%X\n", bhi_info->bhi_ee);
MHI_LOG("BHI Status = 0x%X\n", bhi_info->bhi_status);
MHI_LOG("BHI Error code = 0x%X { Dbg1 = 0x%X Dbg2 = 0x%X Dbg3 = 0x%X }\n", bhi_info->bhi_errorcode, bhi_info->bhi_errdbg1, bhi_info->bhi_errdbg2, bhi_info->bhi_errdbg3);
MHI_LOG("BHI Serial Number = 0x%X\n", bhi_info->bhi_sernum);
MHI_LOG("BHI SBL Anti-Rollback Ver = 0x%X\n", bhi_info->bhi_sblantirollbackver);
MHI_LOG("BHI Number of Segments = 0x%X\n", bhi_info->bhi_numsegs);
for (index = 0; index < 6; index++)
{
snprintf(str+3*index, sizeof(str)-3*index, "%02x ", bhi_info->bhi_msmhwid[index]);
}
MHI_LOG("BHI MSM HW-Id = %s\n", str);
for (index = 0; index < 24; index++)
{
snprintf(str+3*index, sizeof(str)-3*index, "%02x ", bhi_info->bhi_oempkhash[index]);
}
MHI_LOG("BHI OEM PK Hash = %s\n", str);
}
static u32 bhi_read_reg(struct mhi_controller *mhi_cntrl, u32 offset)
{
u32 out = 0;
int ret = mhi_read_reg(mhi_cntrl, mhi_cntrl->bhi, BHI_EXECENV, &out);
return (ret) ? 0 : out;
}
static int BhiRead(struct mhi_controller *mhi_cntrl, BHI_INFO_TYPE *bhi_info)
{
ULONG index;
memset(bhi_info, 0x00, sizeof(BHI_INFO_TYPE));
/* bhi_ver */
bhi_info->bhi_ver_minor = bhi_read_reg(mhi_cntrl, BHI_BHIVERSION_MINOR);
bhi_info->bhi_ver_major = bhi_read_reg(mhi_cntrl, BHI_BHIVERSION_MINOR);
bhi_info->bhi_image_address_low = bhi_read_reg(mhi_cntrl, BHI_IMGADDR_LOW);
bhi_info->bhi_image_address_high = bhi_read_reg(mhi_cntrl, BHI_IMGADDR_HIGH);
bhi_info->bhi_image_size = bhi_read_reg(mhi_cntrl, BHI_IMGSIZE);
bhi_info->bhi_rsvd1 = bhi_read_reg(mhi_cntrl, BHI_RSVD1);
bhi_info->bhi_imgtxdb = bhi_read_reg(mhi_cntrl, BHI_IMGTXDB);
bhi_info->bhi_rsvd2 = bhi_read_reg(mhi_cntrl, BHI_RSVD2);
bhi_info->bhi_msivec = bhi_read_reg(mhi_cntrl, BHI_INTVEC);
bhi_info->bhi_rsvd3 = bhi_read_reg(mhi_cntrl, BHI_RSVD3);
bhi_info->bhi_ee = bhi_read_reg(mhi_cntrl, BHI_EXECENV);
bhi_info->bhi_status = bhi_read_reg(mhi_cntrl, BHI_STATUS);
bhi_info->bhi_errorcode = bhi_read_reg(mhi_cntrl, BHI_ERRCODE);
bhi_info->bhi_errdbg1 = bhi_read_reg(mhi_cntrl, BHI_ERRDBG1);
bhi_info->bhi_errdbg2 = bhi_read_reg(mhi_cntrl, BHI_ERRDBG2);
bhi_info->bhi_errdbg3 = bhi_read_reg(mhi_cntrl, BHI_ERRDBG3);
bhi_info->bhi_sernum = bhi_read_reg(mhi_cntrl, BHI_SERIALNU);
bhi_info->bhi_sblantirollbackver = bhi_read_reg(mhi_cntrl, BHI_SBLANTIROLLVER);
bhi_info->bhi_numsegs = bhi_read_reg(mhi_cntrl, BHI_NUMSEG);
for (index = 0; index < MSMHWID_NUMDWORDS; index++)
{
bhi_info->bhi_msmhwid[index] = bhi_read_reg(mhi_cntrl, BHI_MSMHWID(index));
}
for (index = 0; index < OEMPKHASH_NUMDWORDS; index++)
{
bhi_info->bhi_oempkhash[index] = bhi_read_reg(mhi_cntrl, BHI_OEMPKHASH(index));
}
bhi_info->bhi_rsvd5 = bhi_read_reg(mhi_cntrl, BHI_RSVD5);
PrintBhiInfo(mhi_cntrl, bhi_info);
/* Check the Execution Environment */
if (!IsPBLExecEnv(bhi_info->bhi_ee))
{
MHI_LOG("E - EE: 0x%X Expected PBL/EDL\n", bhi_info->bhi_ee);
}
/* Return the number of bytes read */
return 0;
}
/* setup rddm vector table for rddm transfer */
static void mhi_rddm_prepare(struct mhi_controller *mhi_cntrl,
struct image_info *img_info)
{
struct mhi_buf *mhi_buf = img_info->mhi_buf;
struct bhi_vec_entry *bhi_vec = img_info->bhi_vec;
int i = 0;
for (i = 0; i < img_info->entries - 1; i++, mhi_buf++, bhi_vec++) {
MHI_VERB("Setting vector:%pad size:%zu\n",
&mhi_buf->dma_addr, mhi_buf->len);
bhi_vec->dma_addr = mhi_buf->dma_addr;
bhi_vec->size = mhi_buf->len;
}
}
/* collect rddm during kernel panic */
static int __mhi_download_rddm_in_panic(struct mhi_controller *mhi_cntrl)
{
int ret;
struct mhi_buf *mhi_buf;
u32 sequence_id;
u32 rx_status;
enum mhi_ee ee;
struct image_info *rddm_image = mhi_cntrl->rddm_image;
const u32 delayus = 2000;
u32 retry = (mhi_cntrl->timeout_ms * 1000) / delayus;
const u32 rddm_timeout_us = 200000;
int rddm_retry = rddm_timeout_us / delayus; /* time to enter rddm */
void __iomem *base = mhi_cntrl->bhie;
MHI_LOG("Entered with pm_state:%s dev_state:%s ee:%s\n",
to_mhi_pm_state_str(mhi_cntrl->pm_state),
TO_MHI_STATE_STR(mhi_cntrl->dev_state),
TO_MHI_EXEC_STR(mhi_cntrl->ee));
/*
* This should only be executing during a kernel panic, we expect all
* other cores to shutdown while we're collecting rddm buffer. After
* returning from this function, we expect device to reset.
*
* Normaly, we would read/write pm_state only after grabbing
* pm_lock, since we're in a panic, skipping it. Also there is no
* gurantee this state change would take effect since
* we're setting it w/o grabbing pmlock, it's best effort
*/
mhi_cntrl->pm_state = MHI_PM_LD_ERR_FATAL_DETECT;
/* update should take the effect immediately */
smp_wmb();
/* setup the RX vector table */
mhi_rddm_prepare(mhi_cntrl, rddm_image);
mhi_buf = &rddm_image->mhi_buf[rddm_image->entries - 1];
MHI_LOG("Starting BHIe programming for RDDM\n");
mhi_write_reg(mhi_cntrl, base, BHIE_RXVECADDR_HIGH_OFFS,
upper_32_bits(mhi_buf->dma_addr));
mhi_write_reg(mhi_cntrl, base, BHIE_RXVECADDR_LOW_OFFS,
lower_32_bits(mhi_buf->dma_addr));
mhi_write_reg(mhi_cntrl, base, BHIE_RXVECSIZE_OFFS, mhi_buf->len);
sequence_id = prandom_u32() & BHIE_RXVECSTATUS_SEQNUM_BMSK;
if (unlikely(!sequence_id))
sequence_id = 1;
mhi_write_reg_field(mhi_cntrl, base, BHIE_RXVECDB_OFFS,
BHIE_RXVECDB_SEQNUM_BMSK, BHIE_RXVECDB_SEQNUM_SHFT,
sequence_id);
MHI_LOG("Trigger device into RDDM mode\n");
mhi_set_mhi_state(mhi_cntrl, MHI_STATE_SYS_ERR);
MHI_LOG("Waiting for device to enter RDDM\n");
while (rddm_retry--) {
ee = mhi_get_exec_env(mhi_cntrl);
if (ee == MHI_EE_RDDM)
break;
udelay(delayus);
}
if (rddm_retry <= 0) {
/* This is a hardware reset, will force device to enter rddm */
MHI_LOG(
"Did not enter RDDM triggering host req. reset to force rddm\n");
mhi_write_reg(mhi_cntrl, mhi_cntrl->regs,
MHI_SOC_RESET_REQ_OFFSET, MHI_SOC_RESET_REQ);
udelay(delayus);
}
ee = mhi_get_exec_env(mhi_cntrl);
MHI_LOG("Waiting for image download completion, current EE:%s\n",
TO_MHI_EXEC_STR(ee));
while (retry--) {
ret = mhi_read_reg_field(mhi_cntrl, base, BHIE_RXVECSTATUS_OFFS,
BHIE_RXVECSTATUS_STATUS_BMSK,
BHIE_RXVECSTATUS_STATUS_SHFT,
&rx_status);
if (ret)
return -EIO;
if (rx_status == BHIE_RXVECSTATUS_STATUS_XFER_COMPL) {
MHI_LOG("RDDM successfully collected\n");
return 0;
}
udelay(delayus);
}
ee = mhi_get_exec_env(mhi_cntrl);
ret = mhi_read_reg(mhi_cntrl, base, BHIE_RXVECSTATUS_OFFS, &rx_status);
MHI_ERR("Did not complete RDDM transfer\n");
MHI_ERR("Current EE:%s\n", TO_MHI_EXEC_STR(ee));
MHI_ERR("RXVEC_STATUS:0x%x, ret:%d\n", rx_status, ret);
return -EIO;
}
/* download ramdump image from device */
int mhi_download_rddm_img(struct mhi_controller *mhi_cntrl, bool in_panic)
{
void __iomem *base = mhi_cntrl->bhie;
rwlock_t *pm_lock = &mhi_cntrl->pm_lock;
struct image_info *rddm_image = mhi_cntrl->rddm_image;
struct mhi_buf *mhi_buf;
int ret;
u32 rx_status;
u32 sequence_id;
if (!rddm_image)
return -ENOMEM;
if (in_panic)
return __mhi_download_rddm_in_panic(mhi_cntrl);
MHI_LOG("Waiting for device to enter RDDM state from EE:%s\n",
TO_MHI_EXEC_STR(mhi_cntrl->ee));
ret = wait_event_timeout(mhi_cntrl->state_event,
mhi_cntrl->ee == MHI_EE_RDDM ||
MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state),
msecs_to_jiffies(mhi_cntrl->timeout_ms));
if (!ret || MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state)) {
MHI_ERR("MHI is not in valid state, pm_state:%s ee:%s\n",
to_mhi_pm_state_str(mhi_cntrl->pm_state),
TO_MHI_EXEC_STR(mhi_cntrl->ee));
return -EIO;
}
mhi_rddm_prepare(mhi_cntrl, mhi_cntrl->rddm_image);
/* vector table is the last entry */
mhi_buf = &rddm_image->mhi_buf[rddm_image->entries - 1];
read_lock_bh(pm_lock);
if (!MHI_REG_ACCESS_VALID(mhi_cntrl->pm_state)) {
read_unlock_bh(pm_lock);
return -EIO;
}
MHI_LOG("Starting BHIe Programming for RDDM\n");
mhi_write_reg(mhi_cntrl, base, BHIE_RXVECADDR_HIGH_OFFS,
upper_32_bits(mhi_buf->dma_addr));
mhi_write_reg(mhi_cntrl, base, BHIE_RXVECADDR_LOW_OFFS,
lower_32_bits(mhi_buf->dma_addr));
mhi_write_reg(mhi_cntrl, base, BHIE_RXVECSIZE_OFFS, mhi_buf->len);
sequence_id = prandom_u32() & BHIE_RXVECSTATUS_SEQNUM_BMSK;
mhi_write_reg_field(mhi_cntrl, base, BHIE_RXVECDB_OFFS,
BHIE_RXVECDB_SEQNUM_BMSK, BHIE_RXVECDB_SEQNUM_SHFT,
sequence_id);
read_unlock_bh(pm_lock);
MHI_LOG("Upper:0x%x Lower:0x%x len:0x%zx sequence:%u\n",
upper_32_bits(mhi_buf->dma_addr),
lower_32_bits(mhi_buf->dma_addr),
mhi_buf->len, sequence_id);
MHI_LOG("Waiting for image download completion\n");
/* waiting for image download completion */
wait_event_timeout(mhi_cntrl->state_event,
MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state) ||
mhi_read_reg_field(mhi_cntrl, base,
BHIE_RXVECSTATUS_OFFS,
BHIE_RXVECSTATUS_STATUS_BMSK,
BHIE_RXVECSTATUS_STATUS_SHFT,
&rx_status) || rx_status,
msecs_to_jiffies(mhi_cntrl->timeout_ms));
if (MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state))
return -EIO;
return (rx_status == BHIE_RXVECSTATUS_STATUS_XFER_COMPL) ? 0 : -EIO;
}
EXPORT_SYMBOL(mhi_download_rddm_img);
static int mhi_fw_load_amss(struct mhi_controller *mhi_cntrl,
const struct mhi_buf *mhi_buf)
{
void __iomem *base = mhi_cntrl->bhie;
rwlock_t *pm_lock = &mhi_cntrl->pm_lock;
u32 tx_status;
read_lock_bh(pm_lock);
if (!MHI_REG_ACCESS_VALID(mhi_cntrl->pm_state)) {
read_unlock_bh(pm_lock);
return -EIO;
}
MHI_LOG("Starting BHIe Programming\n");
mhi_write_reg(mhi_cntrl, base, BHIE_TXVECADDR_HIGH_OFFS,
upper_32_bits(mhi_buf->dma_addr));
mhi_write_reg(mhi_cntrl, base, BHIE_TXVECADDR_LOW_OFFS,
lower_32_bits(mhi_buf->dma_addr));
mhi_write_reg(mhi_cntrl, base, BHIE_TXVECSIZE_OFFS, mhi_buf->len);
mhi_cntrl->sequence_id = prandom_u32() & BHIE_TXVECSTATUS_SEQNUM_BMSK;
mhi_write_reg_field(mhi_cntrl, base, BHIE_TXVECDB_OFFS,
BHIE_TXVECDB_SEQNUM_BMSK, BHIE_TXVECDB_SEQNUM_SHFT,
mhi_cntrl->sequence_id);
read_unlock_bh(pm_lock);
MHI_LOG("Upper:0x%x Lower:0x%x len:0x%zx sequence:%u\n",
upper_32_bits(mhi_buf->dma_addr),
lower_32_bits(mhi_buf->dma_addr),
mhi_buf->len, mhi_cntrl->sequence_id);
MHI_LOG("Waiting for image transfer completion\n");
/* waiting for image download completion */
wait_event_timeout(mhi_cntrl->state_event,
MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state) ||
mhi_read_reg_field(mhi_cntrl, base,
BHIE_TXVECSTATUS_OFFS,
BHIE_TXVECSTATUS_STATUS_BMSK,
BHIE_TXVECSTATUS_STATUS_SHFT,
&tx_status) || tx_status,
msecs_to_jiffies(mhi_cntrl->timeout_ms));
if (MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state))
return -EIO;
return (tx_status == BHIE_TXVECSTATUS_STATUS_XFER_COMPL) ? 0 : -EIO;
}
static int mhi_fw_load_sbl(struct mhi_controller *mhi_cntrl,
dma_addr_t dma_addr,
size_t size)
{
u32 tx_status, val;
u32 ImgTxDb = 0x1;
int i, ret;
void __iomem *base = mhi_cntrl->bhi;
rwlock_t *pm_lock = &mhi_cntrl->pm_lock;
struct {
char *name;
u32 offset;
} error_reg[] = {
{ "ERROR_CODE", BHI_ERRCODE },
{ "ERROR_DBG1", BHI_ERRDBG1 },
{ "ERROR_DBG2", BHI_ERRDBG2 },
{ "ERROR_DBG3", BHI_ERRDBG3 },
{ NULL },
};
MHI_LOG("Starting BHI programming\n");
/* program start sbl download via bhi protocol */
read_lock_bh(pm_lock);
if (!MHI_REG_ACCESS_VALID(mhi_cntrl->pm_state)) {
read_unlock_bh(pm_lock);
goto invalid_pm_state;
}
mhi_write_reg(mhi_cntrl, base, BHI_STATUS, 0);
mhi_write_reg(mhi_cntrl, base, BHI_IMGADDR_HIGH,
upper_32_bits(dma_addr));
mhi_write_reg(mhi_cntrl, base, BHI_IMGADDR_LOW,
lower_32_bits(dma_addr));
mhi_write_reg(mhi_cntrl, base, BHI_IMGSIZE, size);
mhi_write_reg_field(mhi_cntrl, mhi_cntrl->regs, MHICFG, MHICFG_NER_MASK, MHICFG_NER_SHIFT, NUM_MHI_EVT_RINGS);
mhi_write_reg_field(mhi_cntrl, mhi_cntrl->regs, MHICFG, MHICFG_NHWER_MASK, MHICFG_NHWER_SHIFT, NUM_MHI_HW_EVT_RINGS);
mhi_write_reg(mhi_cntrl, mhi_cntrl->bhi, BHI_INTVEC, mhi_cntrl->msi_irq_base);
mhi_write_reg(mhi_cntrl, base, BHI_IMGTXDB, ImgTxDb);
read_unlock_bh(pm_lock);
MHI_LOG("Waiting for image transfer completion\n");
/* waiting for image download completion */
ret = wait_event_timeout(mhi_cntrl->state_event,
MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state) ||
mhi_read_reg_field(mhi_cntrl, base, BHI_STATUS,
BHI_STATUS_MASK, BHI_STATUS_SHIFT,
&tx_status) || tx_status,
msecs_to_jiffies(mhi_cntrl->timeout_ms));
if (MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state))
goto invalid_pm_state;
if (tx_status == BHI_STATUS_ERROR) {
MHI_ERR("Image transfer failed\n");
read_lock_bh(pm_lock);
if (MHI_REG_ACCESS_VALID(mhi_cntrl->pm_state)) {
for (i = 0; error_reg[i].name; i++) {
ret = mhi_read_reg(mhi_cntrl, base,
error_reg[i].offset, &val);
if (ret)
break;
MHI_ERR("reg:%s value:0x%x\n",
error_reg[i].name, val);
}
}
read_unlock_bh(pm_lock);
goto invalid_pm_state;
}
return (tx_status == BHI_STATUS_SUCCESS) ? 0 : -ETIMEDOUT;
invalid_pm_state:
return -EIO;
}
void mhi_free_bhie_table(struct mhi_controller *mhi_cntrl,
struct image_info *image_info)
{
int i;
struct mhi_buf *mhi_buf = image_info->mhi_buf;
for (i = 0; i < image_info->entries; i++, mhi_buf++)
mhi_free_coherent(mhi_cntrl, mhi_buf->len, mhi_buf->buf,
mhi_buf->dma_addr);
kfree(image_info->mhi_buf);
kfree(image_info);
}
int mhi_alloc_bhie_table(struct mhi_controller *mhi_cntrl,
struct image_info **image_info,
size_t alloc_size)
{
size_t seg_size = mhi_cntrl->seg_len;
/* requier additional entry for vec table */
int segments = DIV_ROUND_UP(alloc_size, seg_size) + 1;
int i;
struct image_info *img_info;
struct mhi_buf *mhi_buf;
MHI_LOG("Allocating bytes:%zu seg_size:%zu total_seg:%u\n",
alloc_size, seg_size, segments);
img_info = kzalloc(sizeof(*img_info), GFP_KERNEL);
if (!img_info)
return -ENOMEM;
/* allocate memory for entries */
img_info->mhi_buf = kcalloc(segments, sizeof(*img_info->mhi_buf),
GFP_KERNEL);
if (!img_info->mhi_buf)
goto error_alloc_mhi_buf;
/* allocate and populate vector table */
mhi_buf = img_info->mhi_buf;
for (i = 0; i < segments; i++, mhi_buf++) {
size_t vec_size = seg_size;
/* last entry is for vector table */
if (i == segments - 1)
vec_size = sizeof(struct bhi_vec_entry) * i;
mhi_buf->len = vec_size;
mhi_buf->buf = mhi_alloc_coherent(mhi_cntrl, vec_size,
&mhi_buf->dma_addr, GFP_KERNEL);
if (!mhi_buf->buf)
goto error_alloc_segment;
MHI_LOG("Entry:%d Address:0x%llx size:%zu\n", i,
(unsigned long long)mhi_buf->dma_addr,
mhi_buf->len);
}
img_info->bhi_vec = img_info->mhi_buf[segments - 1].buf;
img_info->entries = segments;
*image_info = img_info;
MHI_LOG("Successfully allocated bhi vec table\n");
return 0;
error_alloc_segment:
for (--i, --mhi_buf; i >= 0; i--, mhi_buf--)
mhi_free_coherent(mhi_cntrl, mhi_buf->len, mhi_buf->buf,
mhi_buf->dma_addr);
error_alloc_mhi_buf:
kfree(img_info);
return -ENOMEM;
}
static void mhi_firmware_copy(struct mhi_controller *mhi_cntrl,
const struct firmware *firmware,
struct image_info *img_info)
{
size_t remainder = firmware->size;
size_t to_cpy;
const u8 *buf = firmware->data;
int i = 0;
struct mhi_buf *mhi_buf = img_info->mhi_buf;
struct bhi_vec_entry *bhi_vec = img_info->bhi_vec;
while (remainder) {
MHI_ASSERT(i >= img_info->entries, "malformed vector table");
to_cpy = min(remainder, mhi_buf->len);
memcpy(mhi_buf->buf, buf, to_cpy);
bhi_vec->dma_addr = mhi_buf->dma_addr;
bhi_vec->size = to_cpy;
MHI_VERB("Setting Vector:0x%llx size: %llu\n",
bhi_vec->dma_addr, bhi_vec->size);
buf += to_cpy;
remainder -= to_cpy;
i++;
bhi_vec++;
mhi_buf++;
}
}
void mhi_fw_load_worker(struct work_struct *work)
{
int ret;
struct mhi_controller *mhi_cntrl;
const char *fw_name;
const struct firmware *firmware;
struct image_info *image_info;
void *buf;
dma_addr_t dma_addr;
size_t size;
mhi_cntrl = container_of(work, struct mhi_controller, fw_worker);
MHI_LOG("Waiting for device to enter PBL from EE:%s\n",
TO_MHI_EXEC_STR(mhi_cntrl->ee));
ret = wait_event_timeout(mhi_cntrl->state_event,
MHI_IN_PBL(mhi_cntrl->ee) ||
MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state),
msecs_to_jiffies(mhi_cntrl->timeout_ms));
if (!ret || MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state)) {
MHI_ERR("MHI is not in valid state\n");
return;
}
MHI_LOG("Device current EE:%s\n", TO_MHI_EXEC_STR(mhi_cntrl->ee));
/* if device in pthru, we do not have to load firmware */
if (mhi_cntrl->ee == MHI_EE_PTHRU)
return;
fw_name = (mhi_cntrl->ee == MHI_EE_EDL) ?
mhi_cntrl->edl_image : mhi_cntrl->fw_image;
if (!fw_name || (mhi_cntrl->fbc_download && (!mhi_cntrl->sbl_size ||
!mhi_cntrl->seg_len))) {
MHI_ERR("No firmware image defined or !sbl_size || !seg_len\n");
return;
}
ret = request_firmware(&firmware, fw_name, mhi_cntrl->dev);
if (ret) {
MHI_ERR("Error loading firmware, ret:%d\n", ret);
return;
}
size = (mhi_cntrl->fbc_download) ? mhi_cntrl->sbl_size : firmware->size;
/* the sbl size provided is maximum size, not necessarily image size */
if (size > firmware->size)
size = firmware->size;
buf = mhi_alloc_coherent(mhi_cntrl, size, &dma_addr, GFP_KERNEL);
if (!buf) {
MHI_ERR("Could not allocate memory for image\n");
release_firmware(firmware);
return;
}
/* load sbl image */
memcpy(buf, firmware->data, size);
ret = mhi_fw_load_sbl(mhi_cntrl, dma_addr, size);
mhi_free_coherent(mhi_cntrl, size, buf, dma_addr);
if (!mhi_cntrl->fbc_download || ret || mhi_cntrl->ee == MHI_EE_EDL)
release_firmware(firmware);
/* error or in edl, we're done */
if (ret || mhi_cntrl->ee == MHI_EE_EDL)
return;
write_lock_irq(&mhi_cntrl->pm_lock);
mhi_cntrl->dev_state = MHI_STATE_RESET;
write_unlock_irq(&mhi_cntrl->pm_lock);
/*
* if we're doing fbc, populate vector tables while
* device transitioning into MHI READY state
*/
if (mhi_cntrl->fbc_download) {
ret = mhi_alloc_bhie_table(mhi_cntrl, &mhi_cntrl->fbc_image,
firmware->size);
if (ret) {
MHI_ERR("Error alloc size of %zu\n", firmware->size);
goto error_alloc_fw_table;
}
MHI_LOG("Copying firmware image into vector table\n");
/* load the firmware into BHIE vec table */
mhi_firmware_copy(mhi_cntrl, firmware, mhi_cntrl->fbc_image);
}
/* transitioning into MHI RESET->READY state */
ret = mhi_ready_state_transition(mhi_cntrl);
MHI_LOG("To Reset->Ready PM_STATE:%s MHI_STATE:%s EE:%s, ret:%d\n",
to_mhi_pm_state_str(mhi_cntrl->pm_state),
TO_MHI_STATE_STR(mhi_cntrl->dev_state),
TO_MHI_EXEC_STR(mhi_cntrl->ee), ret);
if (!mhi_cntrl->fbc_download)
return;
if (ret) {
MHI_ERR("Did not transition to READY state\n");
goto error_read;
}
/* wait for SBL event */
ret = wait_event_timeout(mhi_cntrl->state_event,
mhi_cntrl->ee == MHI_EE_SBL ||
MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state),
msecs_to_jiffies(mhi_cntrl->timeout_ms));
if (!ret || MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state)) {
MHI_ERR("MHI did not enter BHIE\n");
goto error_read;
}
/* start full firmware image download */
image_info = mhi_cntrl->fbc_image;
ret = mhi_fw_load_amss(mhi_cntrl,
/* last entry is vec table */
&image_info->mhi_buf[image_info->entries - 1]);
MHI_LOG("amss fw_load, ret:%d\n", ret);
release_firmware(firmware);
return;
error_read:
mhi_free_bhie_table(mhi_cntrl, mhi_cntrl->fbc_image);
mhi_cntrl->fbc_image = NULL;
error_alloc_fw_table:
release_firmware(firmware);
}
int BhiWrite(struct mhi_controller *mhi_cntrl, void __user *ubuf, size_t size)
{
int ret;
dma_addr_t dma_addr;
void *dma_buf;
MHI_LOG("Device current EE:%s, M:%s, PM:%s\n",
TO_MHI_EXEC_STR(mhi_get_exec_env(mhi_cntrl)),
TO_MHI_STATE_STR(mhi_get_mhi_state(mhi_cntrl)),
to_mhi_pm_state_str(mhi_cntrl->pm_state));
#if 0
if (mhi_get_exec_env(mhi_cntrl) == MHI_EE_EDL && mhi_cntrl->ee != MHI_EE_EDL) {
mhi_cntrl->ee = MHI_EE_EDL;
wait_event_timeout(mhi_cntrl->state_event,
MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state),
msecs_to_jiffies(mhi_cntrl->timeout_ms + 500));
}
#endif
#if 0
if (!MHI_IN_PBL(mhi_cntrl->ee) || MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state)) {
MHI_ERR("MHI is not in valid BHI state\n");
return -EINVAL;
}
#endif
if (mhi_cntrl->ee != MHI_EE_EDL) {
MHI_ERR("MHI is not in EDL state\n");
return -EINVAL;
}
dma_buf = mhi_alloc_coherent(mhi_cntrl, size, &dma_addr, GFP_KERNEL);
if (!dma_buf) {
MHI_ERR("Could not allocate memory for image\n");
return -ENOMEM;
}
ret = copy_from_user(dma_buf, ubuf, size);
if (ret) {
MHI_ERR("IOCTL_BHI_WRITEIMAGE copy buf error, ret = %d\n", ret);
mhi_free_coherent(mhi_cntrl, size, dma_buf, dma_addr);;
return ret;
}
ret = mhi_fw_load_sbl(mhi_cntrl, dma_addr, size);
mhi_free_coherent(mhi_cntrl, size, dma_buf, dma_addr);
if (ret) {
MHI_ERR("ret = %d, ee=%d\n", ret, mhi_cntrl->ee);
goto error_state;
}
write_lock_irq(&mhi_cntrl->pm_lock);
mhi_cntrl->dev_state = MHI_STATE_RESET;
write_unlock_irq(&mhi_cntrl->pm_lock);
/* transitioning into MHI RESET->READY state */
ret = mhi_ready_state_transition(mhi_cntrl);
if (ret) {
MHI_ERR("Did not transition to READY state\n");
goto error_state;
}
MHI_LOG("To Reset->Ready PM_STATE:%s MHI_STATE:%s EE:%s, ret:%d\n",
to_mhi_pm_state_str(mhi_cntrl->pm_state),
TO_MHI_STATE_STR(mhi_cntrl->dev_state),
TO_MHI_EXEC_STR(mhi_cntrl->ee), ret);
/* wait for BHIE event */
ret = wait_event_timeout(mhi_cntrl->state_event,
mhi_cntrl->ee == MHI_EE_FP ||
MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state),
msecs_to_jiffies(mhi_cntrl->timeout_ms));
if (!ret || MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state)) {
MHI_ERR("MHI did not enter Flash Programmer Environment\n");
goto error_state;
}
MHI_LOG("MHI enter Flash Programmer Environment\n");
return 0;
error_state:
MHI_LOG("Device current EE:%s, M:%s\n",
TO_MHI_EXEC_STR(mhi_get_exec_env(mhi_cntrl)),
TO_MHI_STATE_STR(mhi_get_mhi_state(mhi_cntrl)));
return ret;
}
long bhi_get_dev_info(struct mhi_controller *mhi_cntrl, void __user *ubuf)
{
long ret = -EINVAL;
BHI_INFO_TYPE bhi_info;
ret = BhiRead(mhi_cntrl, &bhi_info);
if (ret) {
MHI_ERR("IOCTL_BHI_GETDEVINFO BhiRead error, ret = %ld\n", ret);
return ret;
}
ret = copy_to_user(ubuf, &bhi_info, sizeof(bhi_info));
if (ret) {
MHI_ERR("IOCTL_BHI_GETDEVINFO copy error, ret = %ld\n", ret);
}
return ret;
}
long bhi_write_image(struct mhi_controller *mhi_cntrl, void __user *ubuf)
{
long ret = -EINVAL;
size_t size;
ret = copy_from_user(&size, ubuf, sizeof(size));
if (ret) {
MHI_ERR("IOCTL_BHI_WRITEIMAGE copy size error, ret = %ld\n", ret);
return ret;
}
ret = BhiWrite(mhi_cntrl, ubuf+sizeof(size), size);
if (ret) {
MHI_ERR("IOCTL_BHI_WRITEIMAGE BhiWrite error, ret = %ld\n", ret);
}
return ret;
}

View File

@ -0,0 +1,274 @@
// SPDX-License-Identifier: GPL-2.0-only
/* Copyright (c) 2018-2019, The Linux Foundation. All rights reserved.*/
#include <linux/debugfs.h>
#include <linux/device.h>
#include <linux/dma-direction.h>
#include <linux/dma-mapping.h>
#include <linux/interrupt.h>
#include <linux/list.h>
#include <linux/of.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/termios.h>
#include <linux/wait.h>
#include "mhi.h"
#include "mhi_internal.h"
struct __packed dtr_ctrl_msg {
u32 preamble;
u32 msg_id;
u32 dest_id;
u32 size;
u32 msg;
};
#define CTRL_MAGIC (0x4C525443)
#define CTRL_MSG_DTR BIT(0)
#define CTRL_MSG_RTS BIT(1)
#define CTRL_MSG_DCD BIT(0)
#define CTRL_MSG_DSR BIT(1)
#define CTRL_MSG_RI BIT(3)
#define CTRL_HOST_STATE (0x10)
#define CTRL_DEVICE_STATE (0x11)
#define CTRL_GET_CHID(dtr) (dtr->dest_id & 0xFF)
static int mhi_dtr_tiocmset(struct mhi_controller *mhi_cntrl,
struct mhi_device *mhi_dev,
u32 tiocm)
{
struct dtr_ctrl_msg *dtr_msg = NULL;
struct mhi_chan *dtr_chan = mhi_cntrl->dtr_dev->ul_chan;
spinlock_t *res_lock = &mhi_dev->dev.devres_lock;
u32 cur_tiocm;
int ret = 0;
cur_tiocm = mhi_dev->tiocm & ~(TIOCM_CD | TIOCM_DSR | TIOCM_RI);
tiocm &= (TIOCM_DTR | TIOCM_RTS);
/* state did not changed */
if (cur_tiocm == tiocm)
return 0;
mutex_lock(&dtr_chan->mutex);
dtr_msg = kzalloc(sizeof(*dtr_msg), GFP_KERNEL);
if (!dtr_msg) {
ret = -ENOMEM;
goto tiocm_exit;
}
dtr_msg->preamble = CTRL_MAGIC;
dtr_msg->msg_id = CTRL_HOST_STATE;
dtr_msg->dest_id = mhi_dev->ul_chan_id;
dtr_msg->size = sizeof(u32);
if (tiocm & TIOCM_DTR)
dtr_msg->msg |= CTRL_MSG_DTR;
if (tiocm & TIOCM_RTS)
dtr_msg->msg |= CTRL_MSG_RTS;
/*
* 'minicom -D /dev/mhi_DUN' will send RTS:1 when open, and RTS:0 when exit.
* RTS:0 will prevent modem output AT response.
* But 'busybox microcom' do not send any RTS to modem.
* [75094.969783] mhi_uci_q 0306_00.03.00_DUN: mhi_dtr_tiocmset DTR:0 RTS:1
* [75100.210994] mhi_uci_q 0306_00.03.00_DUN: mhi_dtr_tiocmset DTR:0 RTS:0
*/
dev_dbg(&mhi_dev->dev, "%s DTR:%d RTS:%d\n", __func__,
!!(tiocm & TIOCM_DTR), !!(tiocm & TIOCM_RTS));
reinit_completion(&dtr_chan->completion);
ret = mhi_queue_transfer(mhi_cntrl->dtr_dev, DMA_TO_DEVICE, dtr_msg,
sizeof(*dtr_msg), MHI_EOT);
if (ret)
goto tiocm_exit;
ret = wait_for_completion_timeout(&dtr_chan->completion,
msecs_to_jiffies(mhi_cntrl->timeout_ms));
if (!ret) {
MHI_ERR("Failed to receive transfer callback\n");
ret = -EIO;
goto tiocm_exit;
}
ret = 0;
spin_lock_irq(res_lock);
mhi_dev->tiocm &= ~(TIOCM_DTR | TIOCM_RTS);
mhi_dev->tiocm |= tiocm;
spin_unlock_irq(res_lock);
tiocm_exit:
kfree(dtr_msg);
mutex_unlock(&dtr_chan->mutex);
return ret;
}
long mhi_ioctl(struct mhi_device *mhi_dev, unsigned int cmd, unsigned long arg)
{
struct mhi_controller *mhi_cntrl = mhi_dev->mhi_cntrl;
int ret;
/* ioctl not supported by this controller */
if (!mhi_cntrl->dtr_dev)
return -EIO;
switch (cmd) {
case TIOCMGET:
return mhi_dev->tiocm;
case TIOCMSET:
{
u32 tiocm;
ret = get_user(tiocm, (u32 *)arg);
if (ret)
return ret;
return mhi_dtr_tiocmset(mhi_cntrl, mhi_dev, tiocm);
}
default:
break;
}
return -EINVAL;
}
EXPORT_SYMBOL(mhi_ioctl);
static int mhi_dtr_queue_inbound(struct mhi_controller *mhi_cntrl)
{
struct mhi_device *mhi_dev = mhi_cntrl->dtr_dev;
int nr_trbs = mhi_get_no_free_descriptors(mhi_dev, DMA_FROM_DEVICE);
size_t mtu = mhi_dev->mtu;
void *buf;
int ret = -EIO, i;
for (i = 0; i < nr_trbs; i++) {
buf = kmalloc(mtu, GFP_KERNEL);
if (!buf)
return -ENOMEM;
ret = mhi_queue_transfer(mhi_dev, DMA_FROM_DEVICE, buf, mtu,
MHI_EOT);
if (ret) {
kfree(buf);
return ret;
}
}
return ret;
}
static void mhi_dtr_dl_xfer_cb(struct mhi_device *mhi_dev,
struct mhi_result *mhi_result)
{
struct mhi_controller *mhi_cntrl = mhi_dev->mhi_cntrl;
struct dtr_ctrl_msg *dtr_msg = mhi_result->buf_addr;
u32 chan;
spinlock_t *res_lock;
if (mhi_result->transaction_status == -ENOTCONN) {
kfree(mhi_result->buf_addr);
return;
}
if (mhi_result->bytes_xferd != sizeof(*dtr_msg)) {
MHI_ERR("Unexpected length %zu received\n",
mhi_result->bytes_xferd);
return;
}
MHI_LOG("preamble:0x%x msg_id:%u dest_id:%u msg:0x%x\n",
dtr_msg->preamble, dtr_msg->msg_id, dtr_msg->dest_id,
dtr_msg->msg);
chan = CTRL_GET_CHID(dtr_msg);
if (chan >= mhi_cntrl->max_chan)
goto auto_queue;
mhi_dev = mhi_cntrl->mhi_chan[chan].mhi_dev;
if (!mhi_dev)
goto auto_queue;
res_lock = &mhi_dev->dev.devres_lock;
spin_lock_irq(res_lock);
mhi_dev->tiocm &= ~(TIOCM_CD | TIOCM_DSR | TIOCM_RI);
if (dtr_msg->msg & CTRL_MSG_DCD)
mhi_dev->tiocm |= TIOCM_CD;
if (dtr_msg->msg & CTRL_MSG_DSR)
mhi_dev->tiocm |= TIOCM_DSR;
if (dtr_msg->msg & CTRL_MSG_RI)
mhi_dev->tiocm |= TIOCM_RI;
spin_unlock_irq(res_lock);
auto_queue:
mhi_queue_transfer(mhi_cntrl->dtr_dev, DMA_FROM_DEVICE, mhi_result->buf_addr,
mhi_cntrl->dtr_dev->mtu, MHI_EOT);
}
static void mhi_dtr_ul_xfer_cb(struct mhi_device *mhi_dev,
struct mhi_result *mhi_result)
{
struct mhi_controller *mhi_cntrl = mhi_dev->mhi_cntrl;
struct mhi_chan *dtr_chan = mhi_cntrl->dtr_dev->ul_chan;
MHI_VERB("Received with status:%d\n", mhi_result->transaction_status);
if (!mhi_result->transaction_status)
complete(&dtr_chan->completion);
}
static void mhi_dtr_remove(struct mhi_device *mhi_dev)
{
struct mhi_controller *mhi_cntrl = mhi_dev->mhi_cntrl;
mhi_cntrl->dtr_dev = NULL;
}
static int mhi_dtr_probe(struct mhi_device *mhi_dev,
const struct mhi_device_id *id)
{
struct mhi_controller *mhi_cntrl = mhi_dev->mhi_cntrl;
int ret;
MHI_LOG("Enter for DTR control channel\n");
mhi_dev->mtu = min_t(size_t, id->driver_data, mhi_dev->mtu);
ret = mhi_prepare_for_transfer(mhi_dev);
if (!ret)
mhi_cntrl->dtr_dev = mhi_dev;
if (!ret)
ret = mhi_dtr_queue_inbound(mhi_cntrl);
MHI_LOG("Exit with ret:%d\n", ret);
return ret;
}
static const struct mhi_device_id mhi_dtr_table[] = {
{ .chan = "IP_CTRL", .driver_data = sizeof(struct dtr_ctrl_msg) },
{},
};
static struct mhi_driver mhi_dtr_driver = {
.id_table = mhi_dtr_table,
.remove = mhi_dtr_remove,
.probe = mhi_dtr_probe,
.ul_xfer_cb = mhi_dtr_ul_xfer_cb,
.dl_xfer_cb = mhi_dtr_dl_xfer_cb,
.driver = {
.name = "MHI_DTR",
.owner = THIS_MODULE,
}
};
int __init mhi_dtr_init(void)
{
return mhi_driver_register(&mhi_dtr_driver);
}
void mhi_dtr_exit(void) {
mhi_driver_unregister(&mhi_dtr_driver);
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,362 @@
#ifndef __SDX20_MHI_H
#define __SDX20_MHI_H
#include <linux/types.h>
/* MHI control data structures alloted by the host, including
* channel context array, event context array, command context and rings */
/* Channel context state */
enum mhi_dev_ch_ctx_state {
MHI_DEV_CH_STATE_DISABLED,
MHI_DEV_CH_STATE_ENABLED,
MHI_DEV_CH_STATE_RUNNING,
MHI_DEV_CH_STATE_SUSPENDED,
MHI_DEV_CH_STATE_STOP,
MHI_DEV_CH_STATE_ERROR,
MHI_DEV_CH_STATE_RESERVED,
MHI_DEV_CH_STATE_32BIT = 0x7FFFFFFF
};
/* Channel type */
enum mhi_dev_ch_ctx_type {
MHI_DEV_CH_TYPE_NONE,
MHI_DEV_CH_TYPE_OUTBOUND_CHANNEL,
MHI_DEV_CH_TYPE_INBOUND_CHANNEL,
MHI_DEV_CH_RESERVED
};
/* Channel context type */
struct mhi_dev_ch_ctx {
enum mhi_dev_ch_ctx_state ch_state;
enum mhi_dev_ch_ctx_type ch_type;
uint32_t err_indx;
uint64_t rbase;
uint64_t rlen;
uint64_t rp;
uint64_t wp;
} __packed;
enum mhi_dev_ring_element_type_id {
MHI_DEV_RING_EL_INVALID = 0,
MHI_DEV_RING_EL_NOOP = 1,
MHI_DEV_RING_EL_TRANSFER = 2,
MHI_DEV_RING_EL_RESET = 16,
MHI_DEV_RING_EL_STOP = 17,
MHI_DEV_RING_EL_START = 18,
MHI_DEV_RING_EL_MHI_STATE_CHG = 32,
MHI_DEV_RING_EL_CMD_COMPLETION_EVT = 33,
MHI_DEV_RING_EL_TRANSFER_COMPLETION_EVENT = 34,
MHI_DEV_RING_EL_EE_STATE_CHANGE_NOTIFY = 64,
MHI_DEV_RING_EL_UNDEF
};
enum mhi_dev_ring_state {
RING_STATE_UINT = 0,
RING_STATE_IDLE,
RING_STATE_PENDING,
};
enum mhi_dev_ring_type {
RING_TYPE_CMD = 0,
RING_TYPE_ER,
RING_TYPE_CH,
RING_TYPE_INVAL
};
/* Event context interrupt moderation */
enum mhi_dev_evt_ctx_int_mod_timer {
MHI_DEV_EVT_INT_MODERATION_DISABLED
};
/* Event ring type */
enum mhi_dev_evt_ctx_event_ring_type {
MHI_DEV_EVT_TYPE_DEFAULT,
MHI_DEV_EVT_TYPE_VALID,
MHI_DEV_EVT_RESERVED
};
/* Event ring context type */
struct mhi_dev_ev_ctx {
uint32_t res1:16;
enum mhi_dev_evt_ctx_int_mod_timer intmodt:16;
enum mhi_dev_evt_ctx_event_ring_type ertype;
uint32_t msivec;
uint64_t rbase;
uint64_t rlen;
uint64_t rp;
uint64_t wp;
} __packed;
/* Command context */
struct mhi_dev_cmd_ctx {
uint32_t res1;
uint32_t res2;
uint32_t res3;
uint64_t rbase;
uint64_t rlen;
uint64_t rp;
uint64_t wp;
} __packed;
/* generic context */
struct mhi_dev_gen_ctx {
uint32_t res1;
uint32_t res2;
uint32_t res3;
uint64_t rbase;
uint64_t rlen;
uint64_t rp;
uint64_t wp;
} __packed;
/* Transfer ring element */
struct mhi_dev_transfer_ring_element {
uint64_t data_buf_ptr;
uint32_t len:16;
uint32_t res1:16;
uint32_t chain:1;
uint32_t res2:7;
uint32_t ieob:1;
uint32_t ieot:1;
uint32_t bei:1;
uint32_t res3:5;
enum mhi_dev_ring_element_type_id type:8;
uint32_t res4:8;
} __packed;
/* Command ring element */
/* Command ring No op command */
struct mhi_dev_cmd_ring_op {
uint64_t res1;
uint32_t res2;
uint32_t res3:16;
enum mhi_dev_ring_element_type_id type:8;
uint32_t chid:8;
} __packed;
/* Command ring reset channel command */
struct mhi_dev_cmd_ring_reset_channel_cmd {
uint64_t res1;
uint32_t res2;
uint32_t res3:16;
enum mhi_dev_ring_element_type_id type:8;
uint32_t chid:8;
} __packed;
/* Command ring stop channel command */
struct mhi_dev_cmd_ring_stop_channel_cmd {
uint64_t res1;
uint32_t res2;
uint32_t res3:16;
enum mhi_dev_ring_element_type_id type:8;
uint32_t chid:8;
} __packed;
/* Command ring start channel command */
struct mhi_dev_cmd_ring_start_channel_cmd {
uint64_t res1;
uint32_t seqnum;
uint32_t reliable:1;
uint32_t res2:15;
enum mhi_dev_ring_element_type_id type:8;
uint32_t chid:8;
} __packed;
enum mhi_dev_cmd_completion_code {
MHI_CMD_COMPL_CODE_INVALID = 0,
MHI_CMD_COMPL_CODE_SUCCESS = 1,
MHI_CMD_COMPL_CODE_EOT = 2,
MHI_CMD_COMPL_CODE_OVERFLOW = 3,
MHI_CMD_COMPL_CODE_EOB = 4,
MHI_CMD_COMPL_CODE_UNDEFINED = 16,
MHI_CMD_COMPL_CODE_RING_EL = 17,
MHI_CMD_COMPL_CODE_RES
};
/* Event ring elements */
/* Transfer completion event */
struct mhi_dev_event_ring_transfer_completion {
uint64_t ptr;
uint32_t len:16;
uint32_t res1:8;
enum mhi_dev_cmd_completion_code code:8;
uint32_t res2:16;
enum mhi_dev_ring_element_type_id type:8;
uint32_t chid:8;
} __packed;
/* Command completion event */
struct mhi_dev_event_ring_cmd_completion {
uint64_t ptr;
uint32_t res1:24;
enum mhi_dev_cmd_completion_code code:8;
uint32_t res2:16;
enum mhi_dev_ring_element_type_id type:8;
uint32_t res3:8;
} __packed;
enum mhi_dev_state {
MHI_DEV_RESET_STATE = 0,
MHI_DEV_READY_STATE,
MHI_DEV_M0_STATE,
MHI_DEV_M1_STATE,
MHI_DEV_M2_STATE,
MHI_DEV_M3_STATE,
MHI_DEV_MAX_STATE,
MHI_DEV_SYSERR_STATE = 0xff
};
/* MHI state change event */
struct mhi_dev_event_ring_state_change {
uint64_t ptr;
uint32_t res1:24;
enum mhi_dev_state mhistate:8;
uint32_t res2:16;
enum mhi_dev_ring_element_type_id type:8;
uint32_t res3:8;
} __packed;
enum mhi_dev_execenv {
MHI_DEV_SBL_EE = 1,
MHI_DEV_AMSS_EE = 2,
MHI_DEV_UNRESERVED
};
/* EE state change event */
struct mhi_dev_event_ring_ee_state_change {
uint64_t ptr;
uint32_t res1:24;
enum mhi_dev_execenv execenv:8;
uint32_t res2:16;
enum mhi_dev_ring_element_type_id type:8;
uint32_t res3:8;
} __packed;
/* Generic cmd to parse common details like type and channel id */
struct mhi_dev_ring_generic {
uint64_t ptr;
uint32_t res1:24;
enum mhi_dev_state mhistate:8;
uint32_t res2:16;
enum mhi_dev_ring_element_type_id type:8;
uint32_t chid:8;
} __packed;
struct mhi_config {
uint32_t mhi_reg_len;
uint32_t version;
uint32_t event_rings;
uint32_t channels;
uint32_t chdb_offset;
uint32_t erdb_offset;
};
#define NUM_CHANNELS 128
#define HW_CHANNEL_BASE 100
#define HW_CHANNEL_END 107
#define MHI_ENV_VALUE 2
#define MHI_MASK_ROWS_CH_EV_DB 4
#define TRB_MAX_DATA_SIZE 8192
#define MHI_CTRL_STATE 25
#define IPA_DMA_SYNC 1
#define IPA_DMA_ASYNC 0
/*maximum trasnfer completion events buffer*/
#define MAX_TR_EVENTS 50
/*maximum event requests */
#define MHI_MAX_EVT_REQ 50
/* Possible ring element types */
union mhi_dev_ring_element_type {
struct mhi_dev_cmd_ring_op cmd_no_op;
struct mhi_dev_cmd_ring_reset_channel_cmd cmd_reset;
struct mhi_dev_cmd_ring_stop_channel_cmd cmd_stop;
struct mhi_dev_cmd_ring_start_channel_cmd cmd_start;
struct mhi_dev_transfer_ring_element cmd_transfer;
struct mhi_dev_event_ring_transfer_completion evt_tr_comp;
struct mhi_dev_event_ring_cmd_completion evt_cmd_comp;
struct mhi_dev_event_ring_state_change evt_state_change;
struct mhi_dev_event_ring_ee_state_change evt_ee_state;
struct mhi_dev_ring_generic generic;
};
/* Transfer ring element type */
union mhi_dev_ring_ctx {
struct mhi_dev_cmd_ctx cmd;
struct mhi_dev_ev_ctx ev;
struct mhi_dev_ch_ctx ch;
struct mhi_dev_gen_ctx generic;
};
/* MHI host Control and data address region */
struct mhi_host_addr {
uint32_t ctrl_base_lsb;
uint32_t ctrl_base_msb;
uint32_t ctrl_limit_lsb;
uint32_t ctrl_limit_msb;
uint32_t data_base_lsb;
uint32_t data_base_msb;
uint32_t data_limit_lsb;
uint32_t data_limit_msb;
};
/* MHI physical and virtual address region */
struct mhi_meminfo {
struct device *dev;
uintptr_t pa_aligned;
uintptr_t pa_unaligned;
uintptr_t va_aligned;
uintptr_t va_unaligned;
uintptr_t size;
};
struct mhi_addr {
uint64_t host_pa;
uintptr_t device_pa;
uintptr_t device_va;
size_t size;
dma_addr_t phy_addr;
void *virt_addr;
bool use_ipa_dma;
};
struct mhi_interrupt_state {
uint32_t mask;
uint32_t status;
};
enum mhi_dev_channel_state {
MHI_DEV_CH_UNINT,
MHI_DEV_CH_STARTED,
MHI_DEV_CH_PENDING_START,
MHI_DEV_CH_PENDING_STOP,
MHI_DEV_CH_STOPPED,
MHI_DEV_CH_CLOSED,
};
enum mhi_dev_ch_operation {
MHI_DEV_OPEN_CH,
MHI_DEV_CLOSE_CH,
MHI_DEV_READ_CH,
MHI_DEV_READ_WR,
MHI_DEV_POLL,
};
enum mhi_ctrl_info {
MHI_STATE_CONFIGURED = 0,
MHI_STATE_CONNECTED = 1,
MHI_STATE_DISCONNECTED = 2,
MHI_STATE_INVAL,
};
enum mhi_dev_tr_compl_evt_type {
SEND_EVENT_BUFFER,
SEND_EVENT_RD_OFFSET,
};
enum mhi_dev_transfer_type {
MHI_DEV_DMA_SYNC,
MHI_DEV_DMA_ASYNC,
};
#endif /* _SDX20_MHI_H_ */

View File

@ -0,0 +1,426 @@
#ifndef __SDX20_MHI_H
#define __SDX20_MHI_H
#include <linux/types.h>
/* MHI control data structures alloted by the host, including
* channel context array, event context array, command context and rings */
/* Channel context state */
enum mhi_dev_ch_ctx_state {
MHI_DEV_CH_STATE_DISABLED,
MHI_DEV_CH_STATE_ENABLED,
MHI_DEV_CH_STATE_RUNNING,
MHI_DEV_CH_STATE_SUSPENDED,
MHI_DEV_CH_STATE_STOP,
MHI_DEV_CH_STATE_ERROR,
MHI_DEV_CH_STATE_RESERVED,
MHI_DEV_CH_STATE_32BIT = 0x7FFFFFFF
};
/* Channel type */
enum mhi_dev_ch_ctx_type {
MHI_DEV_CH_TYPE_NONE,
MHI_DEV_CH_TYPE_OUTBOUND_CHANNEL,
MHI_DEV_CH_TYPE_INBOUND_CHANNEL,
MHI_DEV_CH_RESERVED
};
/* Channel context type */
struct mhi_dev_ch_ctx {
enum mhi_dev_ch_ctx_state ch_state;
enum mhi_dev_ch_ctx_type ch_type;
uint32_t err_indx;
uint64_t rbase;
uint64_t rlen;
uint64_t rp;
uint64_t wp;
} __packed;
enum mhi_dev_ring_element_type_id {
MHI_DEV_RING_EL_INVALID = 0,
MHI_DEV_RING_EL_NOOP = 1,
MHI_DEV_RING_EL_TRANSFER = 2,
MHI_DEV_RING_EL_RESET = 16,
MHI_DEV_RING_EL_STOP = 17,
MHI_DEV_RING_EL_START = 18,
MHI_DEV_RING_EL_MHI_STATE_CHG = 32,
MHI_DEV_RING_EL_CMD_COMPLETION_EVT = 33,
MHI_DEV_RING_EL_TRANSFER_COMPLETION_EVENT = 34,
MHI_DEV_RING_EL_EE_STATE_CHANGE_NOTIFY = 64,
MHI_DEV_RING_EL_UNDEF
};
enum mhi_dev_ring_state {
RING_STATE_UINT = 0,
RING_STATE_IDLE,
RING_STATE_PENDING,
};
enum mhi_dev_ring_type {
RING_TYPE_CMD = 0,
RING_TYPE_ER,
RING_TYPE_CH,
RING_TYPE_INVAL
};
/* Event context interrupt moderation */
enum mhi_dev_evt_ctx_int_mod_timer {
MHI_DEV_EVT_INT_MODERATION_DISABLED
};
/* Event ring type */
enum mhi_dev_evt_ctx_event_ring_type {
MHI_DEV_EVT_TYPE_DEFAULT,
MHI_DEV_EVT_TYPE_VALID,
MHI_DEV_EVT_RESERVED
};
/* Event ring context type */
struct mhi_dev_ev_ctx {
uint32_t res1:16;
enum mhi_dev_evt_ctx_int_mod_timer intmodt:16;
enum mhi_dev_evt_ctx_event_ring_type ertype;
uint32_t msivec;
uint64_t rbase;
uint64_t rlen;
uint64_t rp;
uint64_t wp;
} __packed;
/* Command context */
struct mhi_dev_cmd_ctx {
uint32_t res1;
uint32_t res2;
uint32_t res3;
uint64_t rbase;
uint64_t rlen;
uint64_t rp;
uint64_t wp;
} __packed;
/* generic context */
struct mhi_dev_gen_ctx {
uint32_t res1;
uint32_t res2;
uint32_t res3;
uint64_t rbase;
uint64_t rlen;
uint64_t rp;
uint64_t wp;
} __packed;
/* Transfer ring element */
struct mhi_dev_transfer_ring_element {
uint64_t data_buf_ptr;
uint32_t len:16;
uint32_t res1:16;
uint32_t chain:1;
uint32_t res2:7;
uint32_t ieob:1;
uint32_t ieot:1;
uint32_t bei:1;
uint32_t res3:5;
enum mhi_dev_ring_element_type_id type:8;
uint32_t res4:8;
} __packed;
/* Command ring element */
/* Command ring No op command */
struct mhi_dev_cmd_ring_op {
uint64_t res1;
uint32_t res2;
uint32_t res3:16;
enum mhi_dev_ring_element_type_id type:8;
uint32_t chid:8;
} __packed;
/* Command ring reset channel command */
struct mhi_dev_cmd_ring_reset_channel_cmd {
uint64_t res1;
uint32_t res2;
uint32_t res3:16;
enum mhi_dev_ring_element_type_id type:8;
uint32_t chid:8;
} __packed;
/* Command ring stop channel command */
struct mhi_dev_cmd_ring_stop_channel_cmd {
uint64_t res1;
uint32_t res2;
uint32_t res3:16;
enum mhi_dev_ring_element_type_id type:8;
uint32_t chid:8;
} __packed;
/* Command ring start channel command */
struct mhi_dev_cmd_ring_start_channel_cmd {
uint64_t res1;
uint32_t seqnum;
uint32_t reliable:1;
uint32_t res2:15;
enum mhi_dev_ring_element_type_id type:8;
uint32_t chid:8;
} __packed;
enum mhi_dev_cmd_completion_code {
MHI_CMD_COMPL_CODE_INVALID = 0,
MHI_CMD_COMPL_CODE_SUCCESS = 1,
MHI_CMD_COMPL_CODE_EOT = 2,
MHI_CMD_COMPL_CODE_OVERFLOW = 3,
MHI_CMD_COMPL_CODE_EOB = 4,
MHI_CMD_COMPL_CODE_UNDEFINED = 16,
MHI_CMD_COMPL_CODE_RING_EL = 17,
MHI_CMD_COMPL_CODE_RES
};
/* Event ring elements */
/* Transfer completion event */
struct mhi_dev_event_ring_transfer_completion {
uint64_t ptr;
uint32_t len:16;
uint32_t res1:8;
enum mhi_dev_cmd_completion_code code:8;
uint32_t res2:16;
enum mhi_dev_ring_element_type_id type:8;
uint32_t chid:8;
} __packed;
/* Command completion event */
struct mhi_dev_event_ring_cmd_completion {
uint64_t ptr;
uint32_t res1:24;
enum mhi_dev_cmd_completion_code code:8;
uint32_t res2:16;
enum mhi_dev_ring_element_type_id type:8;
uint32_t res3:8;
} __packed;
enum mhi_dev_state {
MHI_DEV_RESET_STATE = 0,
MHI_DEV_READY_STATE,
MHI_DEV_M0_STATE,
MHI_DEV_M1_STATE,
MHI_DEV_M2_STATE,
MHI_DEV_M3_STATE,
MHI_DEV_MAX_STATE,
MHI_DEV_SYSERR_STATE = 0xff
};
/* MHI state change event */
struct mhi_dev_event_ring_state_change {
uint64_t ptr;
uint32_t res1:24;
enum mhi_dev_state mhistate:8;
uint32_t res2:16;
enum mhi_dev_ring_element_type_id type:8;
uint32_t res3:8;
} __packed;
enum mhi_dev_execenv {
MHI_DEV_SBL_EE = 1,
MHI_DEV_AMSS_EE = 2,
MHI_DEV_UNRESERVED
};
/* EE state change event */
struct mhi_dev_event_ring_ee_state_change {
uint64_t ptr;
uint32_t res1:24;
enum mhi_dev_execenv execenv:8;
uint32_t res2:16;
enum mhi_dev_ring_element_type_id type:8;
uint32_t res3:8;
} __packed;
/* Generic cmd to parse common details like type and channel id */
struct mhi_dev_ring_generic {
uint64_t ptr;
uint32_t res1:24;
enum mhi_dev_state mhistate:8;
uint32_t res2:16;
enum mhi_dev_ring_element_type_id type:8;
uint32_t chid:8;
} __packed;
struct mhi_config {
uint32_t mhi_reg_len;
uint32_t version;
uint32_t event_rings;
uint32_t channels;
uint32_t chdb_offset;
uint32_t erdb_offset;
};
#define NUM_CHANNELS 128
#define HW_CHANNEL_BASE 100
#define HW_CHANNEL_END 107
#define MHI_ENV_VALUE 2
#define MHI_MASK_ROWS_CH_EV_DB 4
#define TRB_MAX_DATA_SIZE 8192
#define MHI_CTRL_STATE 25
#define IPA_DMA_SYNC 1
#define IPA_DMA_ASYNC 0
/*maximum trasnfer completion events buffer*/
#define MAX_TR_EVENTS 50
/*maximum event requests */
#define MHI_MAX_EVT_REQ 50
/* Possible ring element types */
union mhi_dev_ring_element_type {
struct mhi_dev_cmd_ring_op cmd_no_op;
struct mhi_dev_cmd_ring_reset_channel_cmd cmd_reset;
struct mhi_dev_cmd_ring_stop_channel_cmd cmd_stop;
struct mhi_dev_cmd_ring_start_channel_cmd cmd_start;
struct mhi_dev_transfer_ring_element tre;
struct mhi_dev_event_ring_transfer_completion evt_tr_comp;
struct mhi_dev_event_ring_cmd_completion evt_cmd_comp;
struct mhi_dev_event_ring_state_change evt_state_change;
struct mhi_dev_event_ring_ee_state_change evt_ee_state;
struct mhi_dev_ring_generic generic;
};
/* Transfer ring element type */
union mhi_dev_ring_ctx {
struct mhi_dev_cmd_ctx cmd;
struct mhi_dev_ev_ctx ev;
struct mhi_dev_ch_ctx ch;
struct mhi_dev_gen_ctx generic;
};
/* MHI host Control and data address region */
struct mhi_host_addr {
uint32_t ctrl_base_lsb;
uint32_t ctrl_base_msb;
uint32_t ctrl_limit_lsb;
uint32_t ctrl_limit_msb;
uint32_t data_base_lsb;
uint32_t data_base_msb;
uint32_t data_limit_lsb;
uint32_t data_limit_msb;
};
/* MHI physical and virtual address region */
struct mhi_meminfo {
struct device *dev;
uintptr_t pa_aligned;
uintptr_t pa_unaligned;
uintptr_t va_aligned;
uintptr_t va_unaligned;
uintptr_t size;
};
struct mhi_addr {
uint64_t host_pa;
uintptr_t device_pa;
uintptr_t device_va;
size_t size;
dma_addr_t phy_addr;
void *virt_addr;
bool use_ipa_dma;
};
struct mhi_interrupt_state {
uint32_t mask;
uint32_t status;
};
enum mhi_dev_channel_state {
MHI_DEV_CH_UNINT,
MHI_DEV_CH_STARTED,
MHI_DEV_CH_PENDING_START,
MHI_DEV_CH_PENDING_STOP,
MHI_DEV_CH_STOPPED,
MHI_DEV_CH_CLOSED,
};
enum mhi_dev_ch_operation {
MHI_DEV_OPEN_CH,
MHI_DEV_CLOSE_CH,
MHI_DEV_READ_CH,
MHI_DEV_READ_WR,
MHI_DEV_POLL,
};
enum mhi_ctrl_info {
MHI_STATE_CONFIGURED = 0,
MHI_STATE_CONNECTED = 1,
MHI_STATE_DISCONNECTED = 2,
MHI_STATE_INVAL,
};
enum mhi_dev_tr_compl_evt_type {
SEND_EVENT_BUFFER,
SEND_EVENT_RD_OFFSET,
};
enum mhi_dev_transfer_type {
MHI_DEV_DMA_SYNC,
MHI_DEV_DMA_ASYNC,
};
#if 0
/* SW channel client list */
enum mhi_client_channel {
MHI_CLIENT_LOOPBACK_OUT = 0,
MHI_CLIENT_LOOPBACK_IN = 1,
MHI_CLIENT_SAHARA_OUT = 2,
MHI_CLIENT_SAHARA_IN = 3,
MHI_CLIENT_DIAG_OUT = 4,
MHI_CLIENT_DIAG_IN = 5,
MHI_CLIENT_SSR_OUT = 6,
MHI_CLIENT_SSR_IN = 7,
MHI_CLIENT_QDSS_OUT = 8,
MHI_CLIENT_QDSS_IN = 9,
MHI_CLIENT_EFS_OUT = 10,
MHI_CLIENT_EFS_IN = 11,
MHI_CLIENT_MBIM_OUT = 12,
MHI_CLIENT_MBIM_IN = 13,
MHI_CLIENT_QMI_OUT = 14,
MHI_CLIENT_QMI_IN = 15,
MHI_CLIENT_IP_CTRL_0_OUT = 16,
MHI_CLIENT_IP_CTRL_0_IN = 17,
MHI_CLIENT_IP_CTRL_1_OUT = 18,
MHI_CLIENT_IP_CTRL_1_IN = 19,
MHI_CLIENT_DCI_OUT = 20,
MHI_CLIENT_DCI_IN = 21,
MHI_CLIENT_IP_CTRL_3_OUT = 22,
MHI_CLIENT_IP_CTRL_3_IN = 23,
MHI_CLIENT_IP_CTRL_4_OUT = 24,
MHI_CLIENT_IP_CTRL_4_IN = 25,
MHI_CLIENT_IP_CTRL_5_OUT = 26,
MHI_CLIENT_IP_CTRL_5_IN = 27,
MHI_CLIENT_IP_CTRL_6_OUT = 28,
MHI_CLIENT_IP_CTRL_6_IN = 29,
MHI_CLIENT_IP_CTRL_7_OUT = 30,
MHI_CLIENT_IP_CTRL_7_IN = 31,
MHI_CLIENT_DUN_OUT = 32,
MHI_CLIENT_DUN_IN = 33,
MHI_CLIENT_IP_SW_0_OUT = 34,
MHI_CLIENT_IP_SW_0_IN = 35,
MHI_CLIENT_IP_SW_1_OUT = 36,
MHI_CLIENT_IP_SW_1_IN = 37,
MHI_CLIENT_IP_SW_2_OUT = 38,
MHI_CLIENT_IP_SW_2_IN = 39,
MHI_CLIENT_IP_SW_3_OUT = 40,
MHI_CLIENT_IP_SW_3_IN = 41,
MHI_CLIENT_CSVT_OUT = 42,
MHI_CLIENT_CSVT_IN = 43,
MHI_CLIENT_SMCT_OUT = 44,
MHI_CLIENT_SMCT_IN = 45,
MHI_CLIENT_IP_SW_4_OUT = 46,
MHI_CLIENT_IP_SW_4_IN = 47,
MHI_MAX_SOFTWARE_CHANNELS = 48,
MHI_CLIENT_TEST_OUT = 60,
MHI_CLIENT_TEST_IN = 61,
MHI_CLIENT_RESERVED_1_LOWER = 62,
MHI_CLIENT_RESERVED_1_UPPER = 99,
MHI_CLIENT_IP_HW_0_OUT = 100,
MHI_CLIENT_IP_HW_0_IN = 101,
MHI_CLIENT_RESERVED_2_LOWER = 102,
MHI_CLIENT_RESERVED_2_UPPER = 127,
MHI_MAX_CHANNELS = 102,
};
#endif
#endif /* _SDX20_MHI_H_ */

View File

@ -0,0 +1,33 @@
menu "MHI device support"
config MHI_NETDEV
tristate "MHI NETDEV"
depends on MHI_BUS
help
MHI based net device driver for transferring IP traffic
between host and modem. By enabling this driver, clients
can transfer data using standard network interface. Over
the air traffic goes thru mhi netdev interface.
config MHI_UCI
tristate "MHI UCI"
depends on MHI_BUS
help
MHI based uci driver is for transferring data between host and
modem using standard file operations from user space. Open, read,
write, ioctl, and close operations are supported by this driver.
Please check mhi_uci_match_table for all supported channels that
are exposed to userspace.
config MHI_SATELLITE
tristate "MHI SATELLITE"
depends on MHI_BUS
help
MHI proxy satellite device driver enables NON-HLOS MHI satellite
drivers to communicate with device over PCIe link without host
involvement. Host facilitates propagation of events from device
to NON-HLOS MHI satellite drivers, channel states, and power
management over IPC communication. It helps in HLOS power
savings.
endmenu

View File

@ -0,0 +1,3 @@
obj-$(CONFIG_MHI_NETDEV) +=mhi_netdev.o
obj-$(CONFIG_MHI_UCI) +=mhi_uci.o
obj-$(CONFIG_MHI_SATELLITE) +=mhi_satellite.o

Some files were not shown because too many files have changed in this diff Show More