🗽 Sync 2026-06-04 16:32:58

This commit is contained in:
github-actions[bot] committed 2026-06-04 16:32:58 +08:00
1 parent b079efab10
commit b550deb144
76 files changed
+17971 -26348

No files matched your search

+22
View File
@@ -0,0 +1,22 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=fibocom-dial
PKG_RELEASE:=3
include $(INCLUDE_DIR)/package.mk
define Package/fibocom-dial
SECTION:=net
CATEGORY:=Network
SUBMENU:=WWAN
TITLE:=Dialing tool for Fibocom WWAN modules
endef
define Package/fibocom-dial/install
$(INSTALL_DIR) $(1)/usr/bin
$(INSTALL_BIN) $(PKG_BUILD_DIR)/fibocom-dial $(1)/usr/bin
$(INSTALL_BIN) $(PKG_BUILD_DIR)/fibo_qmimsg_server $(1)/usr/bin/fibocom-qmimsg-server
$(INSTALL_BIN) $(PKG_BUILD_DIR)/multi-pdn-manager $(1)/usr/bin/fibocom-multi-pdn-manager
endef
$(eval $(call BuildPackage,fibocom-dial))
@@ -0,0 +1,42 @@
--- a/QmiWwanCM.c
+++ b/QmiWwanCM.c
@@ -8,7 +8,7 @@ typedef unsigned short sa_family_t;
#include "QMIThread.h"
#ifdef CONFIG_QMIWWAN
-#define FIBO_QMI_PROXY "fibo_qmimsg_server"
+#define FIBO_QMI_PROXY "fibocom-qmimsg-server"
static int cdc_wdm_fd = -1;
static UCHAR GetQCTLTransactionId(void)
--- a/fibo_qmimsg_server.c
+++ b/fibo_qmimsg_server.c
@@ -984,7 +984,7 @@ qmi_proxy_init_fail:
}
//2021-06-03 willa.liu@fibocom.com changed begin
static void qmi_start_server(void) {
- qmi_proxy_server_fd = create_local_server("fibo_qmimsg_server");
+ qmi_proxy_server_fd = create_local_server("fibocom-qmimsg-server");
printf("%s: qmi_proxy_server_fd = %d\n", __func__, qmi_proxy_server_fd);
if (qmi_proxy_server_fd == -1) {
dprintf("%s Failed to create %s, errno: %d (%s)\n", __func__, "fibocom-qmi-proxy", errno, strerror(errno));
--- a/multi-pdn-manager.c
+++ b/multi-pdn-manager.c
@@ -812,7 +812,7 @@ int main(int argc, char *argv[])
{
dbg_time("The Fibocom multi-pdn-manager is already running! So killall multi-pdn-manager!");
system("killall -9 fibocom-dial");
- system("killall -9 multi-pdn-manager");
+ system("killall -9 fibocom-multi-pdn-manager");
}
}
else
@@ -846,7 +846,7 @@ int main(int argc, char *argv[])
if (is_pcie_dial())
{
dbg_time("start qmi proxy server");
- //system("./fibo_qmimsg_server -d /dev/mhi_QMI0 &");
+ //system("./fibocom-qmimsg-server -d /dev/mhi_QMI0 &");
}
while (1)
{
+275
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
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
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
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);
}
File diff suppressed because it is too large. Load diff
+42
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
File diff suppressed because it is too large. Load diff
+260
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
+426
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,
};
+63
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
+28
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>
+722
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;
}
/**
* @}
*/
+167
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);
}
/**
* @}
*/
+5
View File
@@ -0,0 +1,5 @@
#ifndef __DHCP_H__
#define __DHCP_H__
int do_dhcp(char *iname);
#endif //__DHCP_H__
+515
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);
}
+100
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);
}
+106
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
+247
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;
}
+25
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
+748
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;
}
+53
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__
+202
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
+556
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;
}
/**
* @}
*/
+351
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);
}
/**
* @}
*/
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
+67
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=
+438
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;
}
+160
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;
}
+17
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
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);
}
+421
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
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
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
View File
@@ -1,47 +0,0 @@
#
# 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))
-38
View File
@@ -1,38 +0,0 @@
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
-47
View File
@@ -1,47 +0,0 @@
#
# 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:=1.3.8
PKG_RELEASE:=4
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
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
View File
@@ -1,34 +0,0 @@
#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
View File
@@ -1,36 +0,0 @@
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
-132
View File
@@ -1,132 +0,0 @@
Release Notes
[V1.3.8]
Date: 27/06/2024
enhancement:
1. support SDX7x's PID/VID
Release Notes
[V1.3.7]
Date: 27/03/2024
enhancement:
1. support SDX35's PID/VID
2. support IPQ QSDK MHI used with rmnetdata driver
Release Notes
[V1.3.6]
Date: 01/08/2023
enhancement:
1. support Linux Kernel V6.4
2. support change mtu
fix:
1. fix compile error on ipq's spf12.x
Release Notes
[V1.3.5]
Date: 25/02/2023
enhancement:
1. support efuse SDX sleep
2. support IPQ9574 SFE
fix:
1. fix cannot find the node when dialing. Nodes in the /sys/bus/mhi_q/devices directory named hex
[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:
-13
View File
@@ -1,13 +0,0 @@
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
-1
View File
@@ -1 +0,0 @@
obj-$(CONFIG_MHI_QTI) += mhi_qti.o mhi_arch_qti.o
-275
View File
@@ -1,275 +0,0 @@
// 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;
}
-715
View File
@@ -1,715 +0,0 @@
/* 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__);
}
-92
View File
@@ -1,92 +0,0 @@
/* 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
-44
View File
@@ -1,44 +0,0 @@
/* 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_ */
-1
View File
@@ -1 +0,0 @@
obj-$(CONFIG_MHI_BUS) +=mhi_init.o mhi_main.o mhi_pm.o mhi_boot.o mhi_dtr.o
-908
View File
@@ -1,908 +0,0 @@
/* 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.8"
#define ENABLE_MHI_MON
//#define ENABLE_IP_SW0
// #define ENABLE_ADPL
// #define ENABLE_QDSS
#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_IP_HW_QDSS = 103,
// 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,
#ifdef ENABLE_ADPL
ADPL_EVT_RING,
#endif
#ifdef ENABLE_QDSS
QDSS_EVT_RING,
#endif
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 4
#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
#ifdef ENABLE_ADPL
#define NUM_MHI_ADPL_RING_ELEMENTS 256
#endif
#ifdef ENABLE_QDSS
#define NUM_MHI_QDSS_RING_ELEMENTS 256
#endif
/*
* 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_ */
-860
View File
@@ -1,860 +0,0 @@
// 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);
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(6, 1, 0))
sequence_id = get_random_u32() & BHIE_RXVECSTATUS_SEQNUM_BMSK;
#else
sequence_id = prandom_u32() & BHIE_RXVECSTATUS_SEQNUM_BMSK;
#endif
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);
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(6, 1, 0))
sequence_id = get_random_u32() & BHIE_RXVECSTATUS_SEQNUM_BMSK;
#else
sequence_id = prandom_u32() & BHIE_RXVECSTATUS_SEQNUM_BMSK;
#endif
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);
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(6, 1, 0))
mhi_cntrl->sequence_id = get_random_u32() & BHIE_TXVECSTATUS_SEQNUM_BMSK;
#else
mhi_cntrl->sequence_id = prandom_u32() & BHIE_TXVECSTATUS_SEQNUM_BMSK;
#endif
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);
/* error or in edl, we're done */
if (ret || mhi_cntrl->ee == MHI_EE_EDL) {
release_firmware(firmware);
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) {
release_firmware(firmware);
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;
}
if (size <= 0) {
MHI_ERR("IOCTL_BHI_WRITEIMAGE copy size error, size\n");
return -EINVAL;
}
ret = BhiWrite(mhi_cntrl, ubuf+sizeof(size), size);
if (ret) {
MHI_ERR("IOCTL_BHI_WRITEIMAGE BhiWrite error, ret = %ld\n", ret);
}
return ret;
}
-274
View File
@@ -1,274 +0,0 @@
// 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
-362
View File
@@ -1,362 +0,0 @@
#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_ */
-426
View File
@@ -1,426 +0,0 @@
#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_ */
-33
View File
@@ -1,33 +0,0 @@
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
-3
View File
@@ -1,3 +0,0 @@
obj-$(CONFIG_MHI_NETDEV) +=mhi_netdev.o
obj-$(CONFIG_MHI_UCI) +=mhi_uci.o
obj-$(CONFIG_MHI_SATELLITE) +=mhi_satellite.o
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
-981
View File
@@ -1,981 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-only
/* Copyright (c) 2018-2019, The Linux Foundation. All rights reserved.*/
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/dma-direction.h>
#include <linux/errno.h>
#include <linux/fs.h>
#include <linux/mm.h>
#include <linux/delay.h>
#include <linux/version.h>
#if 1
static inline void *ipc_log_context_create(int max_num_pages,
const char *modname, uint16_t user_version)
{ return NULL; }
static inline int ipc_log_string(void *ilctxt, const char *fmt, ...)
{ return -EINVAL; }
#endif
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of_device.h>
#include <linux/poll.h>
#include <linux/slab.h>
#include <linux/types.h>
#include <linux/sched.h>
#include <linux/wait.h>
#include <linux/uaccess.h>
#include <linux/tty.h>
#include "../core/mhi.h"
#define DEVICE_NAME "mhi"
#define MHI_UCI_DRIVER_NAME "mhi_uci_q"
struct uci_chan {
wait_queue_head_t wq;
spinlock_t lock;
struct list_head pending; /* user space waiting to read */
struct uci_buf *cur_buf; /* current buffer user space reading */
size_t rx_size;
};
struct uci_buf {
struct page *page;
void *data;
size_t len;
unsigned nr_trb;
struct list_head node;
};
struct uci_dev {
struct list_head node;
dev_t devt;
struct device *dev;
struct mhi_device *mhi_dev;
const char *chan;
struct mutex mutex; /* sync open and close */
struct mutex r_mutex;
struct mutex w_mutex;
struct uci_chan ul_chan;
struct uci_chan dl_chan;
size_t mtu;
int ref_count;
bool enabled;
unsigned rx_error;
unsigned nr_trb;
unsigned nr_trbs;
struct uci_buf *uci_buf;
struct ktermios termios;
size_t bytes_xferd;
};
struct mhi_uci_drv {
struct list_head head;
struct mutex lock;
struct class *class;
int major;
dev_t dev_t;
};
static int uci_msg_lvl = MHI_MSG_LVL_ERROR;
module_param( uci_msg_lvl, uint, S_IRUGO | S_IWUSR);
#define MSG_VERB(fmt, ...) do { \
if (uci_msg_lvl <= MHI_MSG_LVL_VERBOSE) \
pr_err("[D][%s] " fmt, __func__, ##__VA_ARGS__); \
} while (0)
#define MSG_LOG(fmt, ...) do { \
if (uci_msg_lvl <= MHI_MSG_LVL_INFO) \
pr_err("[I][%s] " fmt, __func__, ##__VA_ARGS__); \
} while (0)
#define MSG_ERR(fmt, ...) do { \
if (uci_msg_lvl <= MHI_MSG_LVL_ERROR) \
pr_err("[E][%s] " fmt, __func__, ##__VA_ARGS__); \
} while (0)
#define MAX_UCI_DEVICES (64)
#define QUEC_MHI_UCI_ALWAYS_OPEN //by now, sdx20 can not handle "start-reset-start" operation, so the simply solution is keep start state
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(6, 1, 0))
#ifdef TCGETS2
__weak int user_termios_to_kernel_termios(struct ktermios *k,
struct termios2 __user *u)
{
return copy_from_user(k, u, sizeof(struct termios2));
}
__weak int kernel_termios_to_user_termios(struct termios2 __user *u,
struct ktermios *k)
{
return copy_to_user(u, k, sizeof(struct termios2));
}
__weak int user_termios_to_kernel_termios_1(struct ktermios *k,
struct termios __user *u)
{
return copy_from_user(k, u, sizeof(struct termios));
}
__weak int kernel_termios_to_user_termios_1(struct termios __user *u,
struct ktermios *k)
{
return copy_to_user(u, k, sizeof(struct termios));
}
#else
__weak int user_termios_to_kernel_termios(struct ktermios *k,
struct termios __user *u)
{
return copy_from_user(k, u, sizeof(struct termios));
}
__weak int kernel_termios_to_user_termios(struct termios __user *u,
struct ktermios *k)
{
return copy_to_user(u, k, sizeof(struct termios));
}
#endif /* TCGETS2 */
#endif
static DECLARE_BITMAP(uci_minors, MAX_UCI_DEVICES);
static struct mhi_uci_drv mhi_uci_drv;
static int mhi_queue_inbound(struct uci_dev *uci_dev)
{
struct mhi_device *mhi_dev = uci_dev->mhi_dev;
int nr_trbs = mhi_get_no_free_descriptors(mhi_dev, DMA_FROM_DEVICE);
size_t mtu = uci_dev->mtu;
void *buf;
struct uci_buf *uci_buf;
int ret = -EIO, i;
if (uci_dev->uci_buf == NULL) {
uci_dev->nr_trb = 0;
uci_dev->nr_trbs = (nr_trbs + 1);
uci_dev->uci_buf = kmalloc_array(uci_dev->nr_trbs, sizeof(*uci_buf), GFP_KERNEL);
if (!uci_dev->uci_buf)
return -ENOMEM;
uci_buf = uci_dev->uci_buf;
for (i = 0; i < uci_dev->nr_trbs; i++, uci_buf++) {
uci_buf->page = alloc_pages(GFP_KERNEL, get_order(mtu));
if (!uci_buf->page)
return -ENOMEM;
uci_buf->data = page_address(uci_buf->page);
uci_buf->len = 0;
uci_buf->nr_trb = i;
if (mhi_dev->dl_chan_id == MHI_CLIENT_DUN_IN) {
//MSG_ERR("[%d] = %p\n", i, uci_buf->data);
}
}
}
for (i = 0; i < nr_trbs; i++) {
#if 0
buf = kmalloc(mtu + sizeof(*uci_buf), GFP_KERNEL);
if (!buf)
return -ENOMEM;
uci_buf = buf + mtu;
uci_buf->data = buf;
#else
uci_buf = &uci_dev->uci_buf[i];
buf = uci_buf->data;
#endif
MSG_VERB("Allocated buf %d of %d size %zu\n", i, nr_trbs, mtu);
ret = mhi_queue_transfer(mhi_dev, DMA_FROM_DEVICE, buf, mtu,
MHI_EOT);
if (ret) {
#if 0
kfree(buf);
#endif
MSG_ERR("Failed to queue buffer %d\n", i);
return ret;
}
}
return ret;
}
static long mhi_uci_ioctl(struct file *file,
unsigned int cmd,
unsigned long arg)
{
struct uci_dev *uci_dev = file->private_data;
struct mhi_device *mhi_dev = uci_dev->mhi_dev;
long ret = -ERESTARTSYS;
mutex_lock(&uci_dev->mutex);
if (uci_dev->enabled)
ret = mhi_ioctl(mhi_dev, cmd, arg);
if (uci_dev->enabled) {
switch (cmd) {
case TCGETS:
#ifndef TCGETS2
ret = kernel_termios_to_user_termios((struct termios __user *)arg, &uci_dev->termios);
#else
ret = kernel_termios_to_user_termios_1((struct termios __user *)arg, &uci_dev->termios);
#endif
break;
case TCSETSF:
case TCSETS:
#ifndef TCGETS2
ret = user_termios_to_kernel_termios(&uci_dev->termios, (struct termios __user *)arg);
#else
ret = user_termios_to_kernel_termios_1(&uci_dev->termios, (struct termios __user *)arg);
#endif
break;
case TCFLSH:
ret = 0;
break;
default:
break;
}
}
mutex_unlock(&uci_dev->mutex);
return ret;
}
static int mhi_uci_release(struct inode *inode, struct file *file)
{
struct uci_dev *uci_dev = file->private_data;
mutex_lock(&uci_dev->mutex);
uci_dev->ref_count--;
if (!uci_dev->ref_count) {
struct uci_chan *uci_chan;
MSG_LOG("Last client left, closing node\n");
if (uci_dev->enabled)
mhi_unprepare_from_transfer(uci_dev->mhi_dev);
/* clean inbound channel */
uci_chan = &uci_dev->dl_chan;
if (uci_dev->uci_buf) {
unsigned nr_trb = 0;
for (nr_trb = 0; nr_trb < uci_dev->nr_trbs; nr_trb++) {
if (uci_dev->uci_buf[nr_trb].page)
__free_pages(uci_dev->uci_buf[nr_trb].page, get_order(uci_dev->mtu));
}
kfree(uci_dev->uci_buf);
}
uci_chan->cur_buf = NULL;
if (!uci_dev->enabled) {
MSG_LOG("Node is deleted, freeing dev node\n");
mutex_unlock(&uci_dev->mutex);
mutex_destroy(&uci_dev->mutex);
clear_bit(MINOR(uci_dev->devt), uci_minors);
kfree(uci_dev);
return 0;
}
}
MSG_LOG("exit: ref_count:%d\n", uci_dev->ref_count);
mutex_unlock(&uci_dev->mutex);
return 0;
}
static unsigned int mhi_uci_poll(struct file *file, poll_table *wait)
{
struct uci_dev *uci_dev = file->private_data;
struct mhi_device *mhi_dev = uci_dev->mhi_dev;
struct uci_chan *uci_chan;
unsigned int mask = 0;
poll_wait(file, &uci_dev->dl_chan.wq, wait);
// ADPL and QDSS do not need poll write. xingduo.du 2023-02-16
// poll_wait(file, &uci_dev->ul_chan.wq, wait);
uci_chan = &uci_dev->dl_chan;
spin_lock_bh(&uci_chan->lock);
if (!uci_dev->enabled) {
mask = POLLERR;
} else if (!list_empty(&uci_chan->pending) || uci_chan->cur_buf) {
MSG_VERB("Client can read from node\n");
mask |= POLLIN | POLLRDNORM;
}
spin_unlock_bh(&uci_chan->lock);
// ADPL and QDSS are single channel, ul_chan not be initilized. xingduo.du 2023-02-27
if (mhi_dev->ul_chan) {
poll_wait(file, &uci_dev->ul_chan.wq, wait);
uci_chan = &uci_dev->ul_chan;
spin_lock_bh(&uci_chan->lock);
if (!uci_dev->enabled) {
mask |= POLLERR;
} else if (mhi_get_no_free_descriptors(mhi_dev, DMA_TO_DEVICE) > 0) {
MSG_VERB("Client can write to node\n");
mask |= POLLOUT | POLLWRNORM;
}
if (!uci_dev->enabled)
mask |= POLLHUP;
if (uci_dev->rx_error)
mask |= POLLERR;
spin_unlock_bh(&uci_chan->lock);
}
MSG_LOG("Client attempted to poll, returning mask 0x%x\n", mask);
return mask;
}
static ssize_t mhi_uci_write(struct file *file,
const char __user *buf,
size_t count,
loff_t *offp)
{
struct uci_dev *uci_dev = file->private_data;
struct mhi_device *mhi_dev = uci_dev->mhi_dev;
struct uci_chan *uci_chan = &uci_dev->ul_chan;
size_t bytes_xfered = 0;
int ret, nr_avail;
if (!buf || !count || uci_dev->rx_error)
return -EINVAL;
/* confirm channel is active */
spin_lock_bh(&uci_chan->lock);
if (!uci_dev->enabled) {
spin_unlock_bh(&uci_chan->lock);
return -ERESTARTSYS;
}
MSG_VERB("Enter: to xfer:%zu bytes\n", count);
while (count) {
size_t xfer_size;
void *kbuf;
enum MHI_FLAGS flags;
spin_unlock_bh(&uci_chan->lock);
nr_avail = mhi_get_no_free_descriptors(mhi_dev, DMA_TO_DEVICE);
if ((nr_avail == 0) && (file->f_flags & O_NONBLOCK))
return -EAGAIN;
/* wait for free descriptors */
ret = wait_event_interruptible(uci_chan->wq,
(!uci_dev->enabled) ||
(nr_avail = mhi_get_no_free_descriptors(mhi_dev,
DMA_TO_DEVICE)) > 0);
if (ret == -ERESTARTSYS || !uci_dev->enabled) {
MSG_LOG("Exit signal caught for node or not enabled\n");
return -ERESTARTSYS;
}
xfer_size = min_t(size_t, count, uci_dev->mtu);
kbuf = kmalloc(xfer_size, GFP_KERNEL);
if (!kbuf) {
MSG_ERR("Failed to allocate memory %zu\n", xfer_size);
return -ENOMEM;
}
ret = copy_from_user(kbuf, buf, xfer_size);
if (unlikely(ret)) {
kfree(kbuf);
return ret;
}
spin_lock_bh(&uci_chan->lock);
/* if ring is full after this force EOT */
if (nr_avail > 1 && (count - xfer_size))
flags = MHI_CHAIN;
else
flags = MHI_EOT;
if (uci_dev->enabled)
ret = mhi_queue_transfer(mhi_dev, DMA_TO_DEVICE, kbuf,
xfer_size, flags);
else
ret = -ERESTARTSYS;
if (ret) {
kfree(kbuf);
goto sys_interrupt;
}
bytes_xfered += xfer_size;
count -= xfer_size;
buf += xfer_size;
}
spin_unlock_bh(&uci_chan->lock);
MSG_VERB("Exit: Number of bytes xferred:%zu\n", bytes_xfered);
return bytes_xfered;
sys_interrupt:
spin_unlock_bh(&uci_chan->lock);
return ret;
}
static ssize_t mhi_uci_read(struct file *file,
char __user *buf,
size_t count,
loff_t *ppos)
{
struct uci_dev *uci_dev = file->private_data;
struct mhi_device *mhi_dev = uci_dev->mhi_dev;
struct uci_chan *uci_chan = &uci_dev->dl_chan;
struct uci_buf *uci_buf;
char *ptr;
size_t to_copy;
int ret = 0;
if (!buf || uci_dev->rx_error)
return -EINVAL;
MSG_VERB("Client provided buf len:%zu\n", count);
/* confirm channel is active */
spin_lock_bh(&uci_chan->lock);
if (!uci_dev->enabled) {
spin_unlock_bh(&uci_chan->lock);
return -ERESTARTSYS;
}
/* No data available to read, wait */
if (!uci_chan->cur_buf && list_empty(&uci_chan->pending)) {
MSG_VERB("No data available to read waiting\n");
spin_unlock_bh(&uci_chan->lock);
if (file->f_flags & O_NONBLOCK)
return -EAGAIN;
ret = wait_event_interruptible(uci_chan->wq,
(!uci_dev->enabled ||
!list_empty(&uci_chan->pending)));
if (ret == -ERESTARTSYS) {
MSG_LOG("Exit signal caught for node\n");
return -ERESTARTSYS;
}
spin_lock_bh(&uci_chan->lock);
if (!uci_dev->enabled) {
MSG_LOG("node is disabled\n");
ret = -ERESTARTSYS;
goto read_error;
}
}
/* new read, get the next descriptor from the list */
if (!uci_chan->cur_buf) {
uci_buf = list_first_entry_or_null(&uci_chan->pending,
struct uci_buf, node);
if (unlikely(!uci_buf)) {
ret = -EIO;
goto read_error;
}
if (uci_buf->node.next == LIST_POISON1 || uci_buf->node.prev == LIST_POISON1) {
dump_stack();
ret = -EIO;
MSG_ERR("chan[%d] data=%p, len=%zd, nr_trb=%d\n",
mhi_dev->dl_chan_id, uci_buf->data, uci_buf->len, uci_buf->nr_trb);
goto read_error;
}
list_del(&uci_buf->node);
uci_chan->cur_buf = uci_buf;
uci_chan->rx_size = uci_buf->len;
MSG_VERB("Got pkt of size:%zu\n", uci_chan->rx_size);
}
uci_buf = uci_chan->cur_buf;
spin_unlock_bh(&uci_chan->lock);
/* Copy the buffer to user space */
to_copy = min_t(size_t, count, uci_chan->rx_size);
ptr = uci_buf->data + (uci_buf->len - uci_chan->rx_size);
ret = copy_to_user(buf, ptr, to_copy);
if (ret)
return ret;
MSG_VERB("Copied %zu of %zu bytes\n", to_copy, uci_chan->rx_size);
uci_chan->rx_size -= to_copy;
/* we finished with this buffer, queue it back to hardware */
if (!uci_chan->rx_size) {
spin_lock_bh(&uci_chan->lock);
uci_chan->cur_buf = NULL;
if (uci_dev->enabled)
#if 1 //this can make the address in ring do not change
{
if (uci_buf->page) {
unsigned nr_trb = uci_buf->nr_trb ? (uci_buf->nr_trb - 1) : (uci_dev->nr_trbs - 1);
uci_buf = &uci_dev->uci_buf[nr_trb];
ret = mhi_queue_transfer(mhi_dev, DMA_FROM_DEVICE,
uci_buf->data, uci_dev->mtu,
MHI_EOT);
} else {
kfree(uci_buf);
ret = 0;
}
}
#endif
else
ret = -ERESTARTSYS;
if (ret) {
MSG_ERR("Failed to recycle element for chan:%d , ret=%d\n", mhi_dev->ul_chan_id, ret);
#if 0
kfree(uci_buf->data);
#endif
goto read_error;
}
spin_unlock_bh(&uci_chan->lock);
}
MSG_VERB("Returning %zu bytes\n", to_copy);
return to_copy;
read_error:
spin_unlock_bh(&uci_chan->lock);
return ret;
}
static ssize_t mhi_uci_write_mutex(struct file *file,
const char __user *buf,
size_t count,
loff_t *offp)
{
struct uci_dev *uci_dev = file->private_data;
int ret;
ret = mutex_lock_interruptible(&uci_dev->w_mutex); /*concurrent writes */
if (ret < 0)
return -ERESTARTSYS;
ret = mhi_uci_write(file, buf, count, offp);
mutex_unlock(&uci_dev->w_mutex);
return ret;
}
static ssize_t mhi_uci_read_mutex(struct file *file,
char __user *buf,
size_t count,
loff_t *ppos)
{
struct uci_dev *uci_dev = file->private_data;
int ret;
ret = mutex_lock_interruptible(&uci_dev->r_mutex); /*concurrent reads */
if (ret < 0)
return -ERESTARTSYS;
ret = mhi_uci_read(file, buf, count, ppos);
mutex_unlock(&uci_dev->r_mutex);
return ret;
}
static int mhi_uci_open(struct inode *inode, struct file *filp)
{
struct uci_dev *uci_dev = NULL, *tmp_dev;
int ret = -EIO;
struct uci_chan *dl_chan;
mutex_lock(&mhi_uci_drv.lock);
list_for_each_entry(tmp_dev, &mhi_uci_drv.head, node) {
if (tmp_dev->devt == inode->i_rdev) {
uci_dev = tmp_dev;
break;
}
}
/* could not find a minor node */
if (!uci_dev)
goto error_exit;
mutex_lock(&uci_dev->mutex);
if (!uci_dev->enabled) {
MSG_ERR("Node exist, but not in active state!\n");
goto error_open_chan;
}
uci_dev->ref_count++;
MSG_LOG("Node open, ref counts %u\n", uci_dev->ref_count);
if (uci_dev->ref_count == 1) {
MSG_LOG("Starting channel\n");
ret = mhi_prepare_for_transfer(uci_dev->mhi_dev);
if (ret) {
MSG_ERR("Error starting transfer channels\n");
uci_dev->ref_count--;
goto error_open_chan;
}
ret = mhi_queue_inbound(uci_dev);
if (ret)
goto error_rx_queue;
#ifdef QUEC_MHI_UCI_ALWAYS_OPEN
uci_dev->ref_count++;
#endif
}
filp->private_data = uci_dev;
mutex_unlock(&uci_dev->mutex);
mutex_unlock(&mhi_uci_drv.lock);
return 0;
error_rx_queue:
dl_chan = &uci_dev->dl_chan;
mhi_unprepare_from_transfer(uci_dev->mhi_dev);
if (uci_dev->uci_buf) {
unsigned nr_trb = 0;
for (nr_trb = 0; nr_trb < uci_dev->nr_trbs; nr_trb++) {
if (uci_dev->uci_buf[nr_trb].page)
__free_pages(uci_dev->uci_buf[nr_trb].page, get_order(uci_dev->mtu));
}
kfree(uci_dev->uci_buf);
}
error_open_chan:
mutex_unlock(&uci_dev->mutex);
error_exit:
mutex_unlock(&mhi_uci_drv.lock);
return ret;
}
static const struct file_operations mhidev_fops = {
.open = mhi_uci_open,
.release = mhi_uci_release,
.read = mhi_uci_read_mutex,
.write = mhi_uci_write_mutex,
.poll = mhi_uci_poll,
.unlocked_ioctl = mhi_uci_ioctl,
};
static void mhi_uci_remove(struct mhi_device *mhi_dev)
{
struct uci_dev *uci_dev = mhi_device_get_devdata(mhi_dev);
MSG_LOG("Enter\n");
mutex_lock(&mhi_uci_drv.lock);
mutex_lock(&uci_dev->mutex);
/* disable the node */
spin_lock_irq(&uci_dev->dl_chan.lock);
spin_lock_irq(&uci_dev->ul_chan.lock);
uci_dev->enabled = false;
spin_unlock_irq(&uci_dev->ul_chan.lock);
spin_unlock_irq(&uci_dev->dl_chan.lock);
wake_up(&uci_dev->dl_chan.wq);
wake_up(&uci_dev->ul_chan.wq);
/* delete the node to prevent new opens */
device_destroy(mhi_uci_drv.class, uci_dev->devt);
uci_dev->dev = NULL;
list_del(&uci_dev->node);
#ifdef QUEC_MHI_UCI_ALWAYS_OPEN
if (uci_dev->ref_count > 0)
uci_dev->ref_count--;
#endif
/* safe to free memory only if all file nodes are closed */
if (!uci_dev->ref_count) {
mutex_unlock(&uci_dev->mutex);
mutex_destroy(&uci_dev->mutex);
clear_bit(MINOR(uci_dev->devt), uci_minors);
kfree(uci_dev);
mutex_unlock(&mhi_uci_drv.lock);
return;
}
MSG_LOG("Exit\n");
mutex_unlock(&uci_dev->mutex);
mutex_unlock(&mhi_uci_drv.lock);
}
static int mhi_uci_probe(struct mhi_device *mhi_dev,
const struct mhi_device_id *id)
{
struct uci_dev *uci_dev;
int minor;
char node_name[32];
int dir;
uci_dev = kzalloc(sizeof(*uci_dev), GFP_KERNEL);
if (!uci_dev)
return -ENOMEM;
mutex_init(&uci_dev->mutex);
mutex_init(&uci_dev->r_mutex);
mutex_init(&uci_dev->w_mutex);
uci_dev->mhi_dev = mhi_dev;
minor = find_first_zero_bit(uci_minors, MAX_UCI_DEVICES);
if (minor >= MAX_UCI_DEVICES) {
kfree(uci_dev);
return -ENOSPC;
}
mutex_lock(&uci_dev->mutex);
mutex_lock(&mhi_uci_drv.lock);
uci_dev->devt = MKDEV(mhi_uci_drv.major, minor);
#if 1
if (mhi_dev->mhi_cntrl->cntrl_idx)
uci_dev->dev = device_create(mhi_uci_drv.class, &mhi_dev->dev,
uci_dev->devt, uci_dev,
DEVICE_NAME "_%s%d",
mhi_dev->chan_name, mhi_dev->mhi_cntrl->cntrl_idx);
else
uci_dev->dev = device_create(mhi_uci_drv.class, &mhi_dev->dev,
uci_dev->devt, uci_dev,
DEVICE_NAME "_%s",
mhi_dev->chan_name);
#else
uci_dev->dev = device_create(mhi_uci_drv.class, &mhi_dev->dev,
uci_dev->devt, uci_dev,
DEVICE_NAME "_%04x_%02u.%02u.%02u%s%d",
mhi_dev->dev_id, mhi_dev->domain,
mhi_dev->bus, mhi_dev->slot, "_pipe_",
mhi_dev->ul_chan_id);
#endif
set_bit(minor, uci_minors);
/* create debugging buffer */
snprintf(node_name, sizeof(node_name), "mhi_uci_%04x_%02u.%02u.%02u_%d",
mhi_dev->dev_id, mhi_dev->domain, mhi_dev->bus, mhi_dev->slot,
mhi_dev->ul_chan_id);
for (dir = 0; dir < 2; dir++) {
struct uci_chan *uci_chan = (dir) ?
&uci_dev->ul_chan : &uci_dev->dl_chan;
spin_lock_init(&uci_chan->lock);
init_waitqueue_head(&uci_chan->wq);
INIT_LIST_HEAD(&uci_chan->pending);
}
uci_dev->termios = tty_std_termios;
uci_dev->mtu = min_t(size_t, id->driver_data, mhi_dev->mtu);
mhi_device_set_devdata(mhi_dev, uci_dev);
uci_dev->enabled = true;
list_add(&uci_dev->node, &mhi_uci_drv.head);
mutex_unlock(&mhi_uci_drv.lock);
mutex_unlock(&uci_dev->mutex);
MSG_LOG("channel:%s successfully probed\n", mhi_dev->chan_name);
return 0;
};
static void mhi_ul_xfer_cb(struct mhi_device *mhi_dev,
struct mhi_result *mhi_result)
{
struct uci_dev *uci_dev = mhi_device_get_devdata(mhi_dev);
struct uci_chan *uci_chan = &uci_dev->ul_chan;
MSG_VERB("status:%d xfer_len:%zu\n", mhi_result->transaction_status,
mhi_result->bytes_xferd);
kfree(mhi_result->buf_addr);
if (!mhi_result->transaction_status)
wake_up(&uci_chan->wq);
}
static void mhi_dl_xfer_cb(struct mhi_device *mhi_dev,
struct mhi_result *mhi_result)
{
struct uci_dev *uci_dev = mhi_device_get_devdata(mhi_dev);
struct uci_chan *uci_chan = &uci_dev->dl_chan;
unsigned long flags;
struct uci_buf *buf;
unsigned nr_trb = uci_dev->nr_trb;
buf = &uci_dev->uci_buf[nr_trb];
if (buf == NULL) {
MSG_ERR("buf = NULL");
return;
}
if (buf->nr_trb != nr_trb || buf->data != mhi_result->buf_addr)
{
uci_dev->rx_error++;
MSG_ERR("chan[%d]: uci_buf[%u] = %p , mhi_result[%u] = %p\n",
mhi_dev->dl_chan_id, buf->nr_trb, buf->data, nr_trb, mhi_result->buf_addr);
return;
}
uci_dev->nr_trb++;
if (uci_dev->nr_trb == uci_dev->nr_trbs)
uci_dev->nr_trb = 0;
if (mhi_result->transaction_status == -ENOTCONN) {
return;
}
if (mhi_result->bytes_xferd > uci_dev->mtu || mhi_result->bytes_xferd <= 0)
{
MSG_ERR("chan[%d]: bytes_xferd = %zd , mtu = %zd\n",
mhi_dev->dl_chan_id, mhi_result->bytes_xferd, uci_dev->mtu);
return;
}
if (mhi_result->bytes_xferd > uci_dev->bytes_xferd)
{
uci_dev->bytes_xferd = mhi_result->bytes_xferd;
//MSG_ERR("chan[%d]: bytes_xferd = %zd , mtu = %zd\n",
// mhi_dev->dl_chan_id, mhi_result->bytes_xferd, uci_dev->mtu);
}
MSG_VERB("status:%d receive_len:%zu\n", mhi_result->transaction_status,
mhi_result->bytes_xferd);
spin_lock_irqsave(&uci_chan->lock, flags);
#if 0
buf = mhi_result->buf_addr + uci_dev->mtu;
buf->data = mhi_result->buf_addr;
#endif
buf->len = mhi_result->bytes_xferd;
if (mhi_dev->dl_chan_id == MHI_CLIENT_DUN_IN
|| mhi_dev->dl_chan_id == MHI_CLIENT_QMI_IN
|| mhi_dev->dl_chan_id == MHI_CLIENT_MBIM_IN)
{
struct uci_buf *tmp_buf = NULL;
int skip_buf = 0;
#ifdef QUEC_MHI_UCI_ALWAYS_OPEN
if (uci_dev->ref_count == 1)
skip_buf++;
#endif
if (!skip_buf)
tmp_buf = (struct uci_buf *)kmalloc(buf->len + sizeof(struct uci_buf), GFP_ATOMIC);;
if (tmp_buf) {
tmp_buf->page = NULL;
tmp_buf->data = ((void *)tmp_buf) + sizeof(struct uci_buf);
tmp_buf->len = buf->len;
memcpy(tmp_buf->data, buf->data, buf->len);
}
if (buf) {
struct uci_buf *uci_buf = buf;
unsigned nr_trb = uci_buf->nr_trb ? (uci_buf->nr_trb - 1) : (uci_dev->nr_trbs - 1);
uci_buf = &uci_dev->uci_buf[nr_trb];
mhi_queue_transfer(mhi_dev, DMA_FROM_DEVICE, uci_buf->data, uci_dev->mtu, MHI_EOT);
}
buf = tmp_buf;
}
if (buf)
list_add_tail(&buf->node, &uci_chan->pending);
spin_unlock_irqrestore(&uci_chan->lock, flags);
#ifdef CONFIG_PM_SLEEP
if (mhi_dev->dev.power.wakeup)
__pm_wakeup_event(mhi_dev->dev.power.wakeup, 0);
#endif
wake_up(&uci_chan->wq);
}
// repaire sdx6x module can not read qdb file. xingduo.du 2023-01-18
#define DIAG_MAX_PCIE_PKT_SZ 8192 //define by module
/* .driver_data stores max mtu */
static const struct mhi_device_id mhi_uci_match_table[] = {
{ .chan = "LOOPBACK", .driver_data = 0x1000 },
{ .chan = "SAHARA", .driver_data = 0x4000 },
{ .chan = "EDL", .driver_data = 0x4000 },
{ .chan = "DIAG", .driver_data = DIAG_MAX_PCIE_PKT_SZ },
{ .chan = "MBIM", .driver_data = 0x1000 },
{ .chan = "QMI0", .driver_data = 0x1000 },
{ .chan = "QMI1", .driver_data = 0x1000 },
{ .chan = "DUN", .driver_data = 0x1000 },
#ifdef ENABLE_ADPL
{ .chan = "ADPL", .driver_data = 0x1000 },
#endif
#ifdef ENABLE_QDSS
{ .chan = "QDSS", .driver_data = 0x1000 },
#endif
{},
};
static struct mhi_driver mhi_uci_driver = {
.id_table = mhi_uci_match_table,
.remove = mhi_uci_remove,
.probe = mhi_uci_probe,
.ul_xfer_cb = mhi_ul_xfer_cb,
.dl_xfer_cb = mhi_dl_xfer_cb,
.driver = {
.name = MHI_UCI_DRIVER_NAME,
.owner = THIS_MODULE,
},
};
int mhi_device_uci_init(void)
{
int ret;
ret = register_chrdev(0, MHI_UCI_DRIVER_NAME, &mhidev_fops);
if (ret < 0)
return ret;
mhi_uci_drv.major = ret;
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(6, 4, 0))
mhi_uci_drv.class = class_create(MHI_UCI_DRIVER_NAME);
#else
mhi_uci_drv.class = class_create(THIS_MODULE, MHI_UCI_DRIVER_NAME);
#endif
if (IS_ERR(mhi_uci_drv.class)) {
unregister_chrdev(mhi_uci_drv.major, MHI_UCI_DRIVER_NAME);
return -ENODEV;
}
mutex_init(&mhi_uci_drv.lock);
INIT_LIST_HEAD(&mhi_uci_drv.head);
ret = mhi_driver_register(&mhi_uci_driver);
if (ret) {
class_destroy(mhi_uci_drv.class);
unregister_chrdev(mhi_uci_drv.major, MHI_UCI_DRIVER_NAME);
}
return ret;
}
void mhi_device_uci_exit(void)
{
mhi_driver_unregister(&mhi_uci_driver);
class_destroy(mhi_uci_drv.class);
unregister_chrdev(mhi_uci_drv.major, MHI_UCI_DRIVER_NAME);
}
-31
View File
@@ -1,31 +0,0 @@
root@imx6qsabresd:~# busybox microcom /dev/mhi_DUN
[ 384.652992] [I][mhi_uci_open] Node open, ref counts 1
[ 384.658144] [I][mhi_uci_open] Starting channel
[ 384.662612] [I][__mhi_prepare_channel] Entered: preparing channel:32
[ 384.680397] [I][mhi_dump_tre] carl_ev evt_cmd_comp code=1
[ 384.685890] [I][__mhi_prepare_channel] Chan:32 successfully moved to start state
[ 384.693312] [I][__mhi_prepare_channel] Entered: preparing channel:33
[ 384.708692] [I][mhi_dump_tre] carl_ev evt_cmd_comp code=1
[ 384.714324] [I][__mhi_prepare_channel] Chan:33 successfully moved to start state
RDY
+CFUN: 1
+CPIN: READY
+QUSIM: 1
+QIND: SMS DONE
+QIND: PB DONE
ati
Quectel
EM20
Revision: EM20GR01A01M4G
OK
at+cpin?
+CPIN: READY
OK
-145
View File
@@ -1,145 +0,0 @@
root@OpenWrt:~# insmod pcie_mhi.ko mhi_mbim_enabled=1
root@OpenWrt:~# dmesg | grep mhi
[ 65.587160] mhi_init Quectel_Linux_PCIE_MHI_Driver_V1.3.0.6
[ 65.597089] mhi_pci_probe pci_dev->name = 0000:01:00.0, domain=0, bus=1, slot=0, vendor=17CB, device=0306
[ 65.602250] mhi_q 0000:01:00.0: BAR 0: assigned [mem 0x20300000-0x20300fff 64bit]
[ 65.611690] mhi_q 0000:01:00.0: enabling device (0140 -> 0142)
[ 65.619307] [I][mhi_init_pci_dev] msi_required = 5, msi_allocated = 5, msi_irq = 63
[ 65.619327] [I][mhi_power_up] dev_state:RESET
[ 65.619331] [I][mhi_async_power_up] Requested to power on
[ 65.619449] [I][mhi_alloc_coherent] size = 114688, dma_handle = 6fca0000
[ 65.619462] [I][mhi_init_dev_ctxt] mhi_ctxt->ctrl_seg = c221e000
[ 65.619731] [I][mhi_async_power_up] dev_state:RESET ee:AMSS
[ 65.619747] [I][mhi_pm_st_worker] Transition to state:READY
[ 65.619760] [I][mhi_pm_st_worker] INVALID_EE -> AMSS
[ 65.619764] [I][mhi_ready_state_transition] Waiting to enter READY state
[ 65.619885] [I][mhi_async_power_up] Power on setup success
[ 65.619897] [I][mhi_pci_probe] Return successful
[ 65.665114] [I][mhi_ready_state_transition] Device in READY State
[ 65.665125] [I][mhi_intvec_threaded_handlr] device ee:AMSS dev_state:READY, pm_state:POR
[ 65.665131] [I][mhi_intvec_threaded_handlr] device ee:AMSS dev_state:READY, INVALID_EE
[ 65.665133] [I][mhi_tryset_pm_state] Transition to pm state from:POR to:POR
[ 65.665137] [I][mhi_init_mmio] Initializing MMIO
[ 65.665142] [I][mhi_init_mmio] CHDBOFF:0x300
[ 65.665151] [I][mhi_init_mmio] ERDBOFF:0x700
[ 65.665156] [I][mhi_init_mmio] Programming all MMIO values.
[ 65.786283] [I][mhi_dump_tre] carl_ev evt_state_change mhistate=2
[ 65.786289] [I][mhi_process_ctrl_ev_ring] MHI state change event to state:M0
[ 65.786295] [I][mhi_pm_m0_transition] Entered With State:READY PM_STATE:POR
[ 65.786300] [I][mhi_tryset_pm_state] Transition to pm state from:POR to:M0
[ 65.789734] [I][mhi_dump_tre] carl_ev evt_ee_state execenv=2
[ 65.789739] [I][mhi_process_ctrl_ev_ring] MHI EE received event:AMSS
[ 65.789756] [I][mhi_pm_st_worker] Transition to state:MISSION MODE
[ 65.789767] [I][mhi_pm_st_worker] INVALID_EE -> AMSS
[ 65.789771] [I][mhi_pm_mission_mode_transition] Processing Mission Mode Transition
[ 65.789787] [I][mhi_init_timesync] No timesync capability found
[ 65.789791] [I][mhi_pm_mission_mode_transition] Adding new devices
[ 65.790570] [I][mhi_dtr_probe] Enter for DTR control channel
[ 65.790577] [I][__mhi_prepare_channel] Entered: preparing channel:18
[ 65.797036] [I][mhi_dump_tre] carl_ev evt_cmd_comp code=1
[ 65.797051] [I][__mhi_prepare_channel] Chan:18 successfully moved to start state
[ 65.797055] [I][__mhi_prepare_channel] Entered: preparing channel:19
[ 65.802457] [I][mhi_dump_tre] carl_ev evt_cmd_comp code=1
[ 65.802469] [I][__mhi_prepare_channel] Chan:19 successfully moved to start state
[ 65.802485] [I][mhi_dtr_probe] Exit with ret:0
[ 65.802748] [I][mhi_netdev_enable_iface] Prepare the channels for transfer
[ 65.802772] [I][__mhi_prepare_channel] Entered: preparing channel:100
[ 65.825279] [I][mhi_dump_tre] carl_ev evt_cmd_comp code=1
[ 65.825293] [I][__mhi_prepare_channel] Chan:100 successfully moved to start state
[ 65.825297] [I][__mhi_prepare_channel] Entered: preparing channel:101
[ 65.835565] [I][mhi_dump_tre] carl_ev evt_cmd_comp code=1
[ 65.835578] [I][__mhi_prepare_channel] Chan:101 successfully moved to start state
[ 65.839141] [I][mhi_netdev_enable_iface] Exited.
[ 65.839875] rmnet_vnd_register_device(rmnet_mhi0.1)=0
[ 65.843278] net rmnet_mhi0 rmnet_mhi0.1: NSS context created
[ 65.861808] [I][mhi_pm_mission_mode_transition] Exit with ret:0
[ 68.625595] [I][__mhi_prepare_channel] Entered: preparing channel:12
[ 68.634610] [I][mhi_dump_tre] carl_ev evt_cmd_comp code=1
[ 68.634622] [I][__mhi_prepare_channel] Chan:12 successfully moved to start state
[ 68.634625] [I][__mhi_prepare_channel] Entered: preparing channel:13
[ 68.644978] [I][mhi_dump_tre] carl_ev evt_cmd_comp code=1
[ 68.644987] [I][__mhi_prepare_channel] Chan:13 successfully moved to start state
[ 69.170666] net rmnet_mhi0: link_state 0x0 -> 0x1
[ 69.177035] [I][mhi_netdev_open] Opened net dev interface
[ 71.655431] [I][mhi_netdev_open] Opened net dev interface
root@OpenWrt:~# ./quectel-CM &
[04-02_04:14:12:134] Quectel_QConnectManager_Linux_V1.6.0.5
[04-02_04:14:12:134] Find /sys/bus/usb/devices/4-1 idVendor=0x2c7c idProduct=0x800, bus=0x004, dev=0x002
[04-02_04:14:12:135] network interface '' or qmidev '' is not exist
[04-02_04:14:12:135] netcard driver = pcie_mhi, driver version = V1.3.0.6
[04-02_04:14:12:135] Modem works in MBIM mode
[04-02_04:14:12:135] apn (null), user (null), passwd (null), auth 0
[04-02_04:14:12:135] IP Proto MBIMContextIPTypeIPv4
[04-02_04:14:12:154] mbim_read_thread is created
sh: can't create /sys/class/net/rmnet_mhi0/mbim/link_state: nonexistent directory
[04-02_04:14:12:156] system(echo 0 > /sys/class/net/rmnet_mhi0/mbim/link_state)=256
[04-02_04:14:12:185] system(ip address flush dev rmnet_mhi0)=0
[04-02_04:14:12:187] system(ip link set dev rmnet_mhi0 down)=0
[04-02_04:14:12:188] mbim_open_device()
[04-02_04:14:12:605] mbim_device_caps_query()
[04-02_04:14:12:610] DeviceId: 869710030002905
[04-02_04:14:12:610] HardwareInfo: 0
[04-02_04:14:12:610] mbim_set_radio_state( 1 )
[04-02_04:14:12:613] HwRadioState: 1, SwRadioState: 1
[04-02_04:14:12:613] mbim_subscriber_status_query()
[04-02_04:14:12:620] SubscriberReadyState NotInitialized -> Initialized
[04-02_04:14:12:620] mbim_register_state_query()
[04-02_04:14:12:625] RegisterState Unknown -> Home
[04-02_04:14:12:625] mbim_packet_service_query()
[04-02_04:14:12:629] PacketServiceState Unknown -> Attached
[04-02_04:14:12:629] mbim_query_connect(sessionID=0)
[04-02_04:14:12:633] ActivationState Unknown -> Deactivated
[04-02_04:14:12:633] mbim_set_connect(onoff=1, sessionID=0)
[ 69.170666] net rmnet_mhi0: link_state 0x0 -> 0x1
[04-02_04:14:12:680] ActivationState Deactivated -> Activated
[ 69.177035] [I][mhi_netdev_open] Opened net dev interface
[04-02_04:14:12:680] mbim_ip_config(sessionID=0)
[04-02_04:14:12:683] < SessionId = 0
[04-02_04:14:12:683] < IPv4ConfigurationAvailable = 0xf
[04-02_04:14:12:683] < IPv6ConfigurationAvailable = 0x0
[04-02_04:14:12:683] < IPv4AddressCount = 0x1
[04-02_04:14:12:683] < IPv4AddressOffset = 0x3c
[04-02_04:14:12:683] < IPv6AddressCount = 0x0
[04-02_04:14:12:683] < IPv6AddressOffset = 0x0
[04-02_04:14:12:683] < IPv4 = 10.129.59.93/30
[04-02_04:14:12:683] < gw = 10.129.59.94
[04-02_04:14:12:683] < dns1 = 211.138.180.2
[04-02_04:14:12:683] < dns2 = 211.138.180.3
[04-02_04:14:12:683] < ipv4 mtu = 1500
sh: can't create /sys/class/net/rmnet_mhi0/mbim/link_state: nonexistent directory
[04-02_04:14:12:684] system(echo 1 > /sys/class/net/rmnet_mhi0/mbim/link_state)=256
[04-02_04:14:12:689] system(ip link set dev rmnet_mhi0 up)=0
[04-02_04:14:12:692] system(ip -4 address flush dev rmnet_mhi0)=0
[04-02_04:14:12:694] system(ip -4 address add 10.129.59.93/30 dev rmnet_mhi0)=0
[04-02_04:14:12:697] system(ip -4 route add default via 10.129.59.94 dev rmnet_mhi0)=0
[04-02_04:14:12:699] system(ip -4 link set dev rmnet_mhi0 mtu 1500)=0
root@OpenWrt:~# ifconfig rmnet_mhi0
rmnet_mhi0 Link encap:UNSPEC HWaddr 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00
UP RUNNING NOARP MTU:1500 Metric:1
RX packets:99379 errors:0 dropped:0 overruns:0 frame:0
TX packets:176569 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:1528181052 (1.4 GiB) TX bytes:62467192 (59.5 MiB)
root@OpenWrt:~# ifconfig rmnet_mhi0.1
rmnet_mhi0.1 Link encap:UNSPEC HWaddr 02-50-F4-00-00-00-00-00-00-00-00-00-00-00-00-00
inet addr:10.129.59.93 Mask:255.255.255.252
inet6 addr: fe80::50:f4ff:fe00:0/64 Scope:Link
UP RUNNING NOARP MTU:1500 Metric:1
RX packets:1089360 errors:0 dropped:0 overruns:0 frame:0
TX packets:176581 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:1521449058 (1.4 GiB) TX bytes:57525792 (54.8 MiB)
# adjust CPU load balancing
root@OpenWrt:~# echo 2 > /sys/class/net/rmnet_mhi0/queues/rx-0/rps_cpus
root@OpenWrt:~# echo 4 > /sys/class/net/rmnet_mhi0.1/queues/rx-0/rps_cpus
root@OpenWrt:~# echo 2000 > /proc/sys/net/core/netdev_max_backlog
root@OpenWrt:~# cat /sys/class/net/rmnet_mhi0/queues/rx-0/rps_cpus
2
root@OpenWrt:~# cat /sys/class/net/rmnet_mhi0.1/queues/rx-0/rps_cpus
4
root@OpenWrt:~# cat /proc/sys/net/core/netdev_max_backlog
2000
-134
View File
@@ -1,134 +0,0 @@
disable ccflags-y += -DCONFIG_MHI_NETDEV_MBIM in pcie_mhi/Makefile
root@OpenWrt:~# insmod pcie_mhi.ko
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
[ 138.497564] mhi_q 0000:01:00.0: BAR 0: assigned [mem 0x20300000-0x20300fff 64bit]
[ 138.506952] mhi_q 0000:01:00.0: enabling device (0140 -> 0142)
[ 138.514562] [I][mhi_init_pci_dev] msi_required = 5, msi_allocated = 5, msi_irq = 63
[ 138.514581] [I][mhi_power_up] dev_state:RESET
[ 138.514587] [I][mhi_async_power_up] Requested to power on
[ 138.514728] [I][mhi_alloc_coherent] size = 114688, dma_handle = 72160000
[ 138.514734] [I][mhi_init_dev_ctxt] mhi_ctxt->ctrl_seg = c221f000
[ 138.515030] [I][mhi_async_power_up] dev_state:RESET ee:AMSS
[ 138.515056] [I][mhi_pm_st_worker] Transition to state:READY
[ 138.515067] [I][mhi_pm_st_worker] INVALID_EE -> AMSS
[ 138.515073] [I][mhi_ready_state_transition] Waiting to enter READY state
[ 138.515210] [I][mhi_async_power_up] Power on setup success
[ 138.515227] [I][mhi_pci_probe] Return successful
[ 138.589013] [I][mhi_ready_state_transition] Device in READY State
[ 138.589029] [I][mhi_intvec_threaded_handlr] device ee:AMSS dev_state:READY, pm_state:POR
[ 138.589038] [I][mhi_intvec_threaded_handlr] device ee:AMSS dev_state:READY, INVALID_EE
[ 138.589041] [I][mhi_tryset_pm_state] Transition to pm state from:POR to:POR
[ 138.589046] [I][mhi_init_mmio] Initializing MMIO
[ 138.589050] [I][mhi_init_mmio] CHDBOFF:0x300
[ 138.589060] [I][mhi_init_mmio] ERDBOFF:0x700
[ 138.589065] [I][mhi_init_mmio] Programming all MMIO values.
[ 138.706124] [I][mhi_dump_tre] carl_ev evt_state_change mhistate=2
[ 138.706132] [I][mhi_process_ctrl_ev_ring] MHI state change event to state:M0
[ 138.706140] [I][mhi_pm_m0_transition] Entered With State:READY PM_STATE:POR
[ 138.706146] [I][mhi_tryset_pm_state] Transition to pm state from:POR to:M0
[ 138.708699] [I][mhi_dump_tre] carl_ev evt_ee_state execenv=2
[ 138.708706] [I][mhi_process_ctrl_ev_ring] MHI EE received event:AMSS
[ 138.708726] [I][mhi_pm_st_worker] Transition to state:MISSION MODE
[ 138.708736] [I][mhi_pm_st_worker] INVALID_EE -> AMSS
[ 138.708742] [I][mhi_pm_mission_mode_transition] Processing Mission Mode Transition
[ 138.708758] [I][mhi_init_timesync] No timesync capability found
[ 138.708764] [I][mhi_pm_mission_mode_transition] Adding new devices
[ 138.709785] [I][mhi_dtr_probe] Enter for DTR control channel
[ 138.709794] [I][__mhi_prepare_channel] Entered: preparing channel:18
[ 138.715378] [I][mhi_dump_tre] carl_ev evt_cmd_comp code=1
[ 138.715397] [I][__mhi_prepare_channel] Chan:18 successfully moved to start state
[ 138.715403] [I][__mhi_prepare_channel] Entered: preparing channel:19
[ 138.720201] [I][mhi_dump_tre] carl_ev evt_cmd_comp code=1
[ 138.720218] [I][__mhi_prepare_channel] Chan:19 successfully moved to start state
[ 138.720236] [I][mhi_dtr_probe] Exit with ret:0
[ 138.720590] [I][mhi_netdev_enable_iface] Prepare the channels for transfer
[ 138.720630] [I][__mhi_prepare_channel] Entered: preparing channel:100
[ 138.757230] [I][mhi_dump_tre] carl_ev evt_cmd_comp code=1
[ 138.757253] [I][__mhi_prepare_channel] Chan:100 successfully moved to start state
[ 138.757259] [I][__mhi_prepare_channel] Entered: preparing channel:101
[ 138.774352] [I][mhi_dump_tre] carl_ev evt_cmd_comp code=1
[ 138.774370] [I][__mhi_prepare_channel] Chan:101 successfully moved to start state
[ 138.778137] [I][mhi_netdev_enable_iface] Exited.
[ 138.779018] rmnet_vnd_register_device(rmnet_mhi0.1)=0
[ 138.782283] net rmnet_mhi0 rmnet_mhi0.1: NSS context created
[ 138.800865] [I][mhi_pm_mission_mode_transition] Exit with ret:0
root@OpenWrt:~# ./quectel-CM &
root@OpenWrt:~# [04-02_04:12:16:477] Quectel_QConnectManager_Linux_V1.6.0.5
[04-02_04:12:16:477] Find /sys/bus/usb/devices/4-1 idVendor=0x2c7c idProduct=0x800, bus=0x004, dev=0x002
[04-02_04:12:16:478] network interface '' or qmidev '' is not exist
[04-02_04:12:16:478] netcard driver = pcie_mhi, driver version = V1.3.0.6
[04-02_04:12:16:479] qmap_mode = 1, qmap_version = 9, qmap_size = 16384, muxid = 0x81, qmap_netcard = rmnet_mhi0.1
[04-02_04:12:16:479] Modem works in QMI mode
[04-02_04:12:16:505] cdc_wdm_fd = 7
[04-02_04:12:17:506] QmiThreadSendQMITimeout pthread_cond_timeout_np timeout
[04-02_04:12:18:516] Get clientWDS = 19
[04-02_04:12:18:520] Get clientDMS = 1
[04-02_04:12:18:524] Get clientNAS = 3
[04-02_04:12:18:527] Get clientUIM = 1
[04-02_04:12:18:531] Get clientWDA = 1
[04-02_04:12:18:535] requestBaseBandVersion RM500QGLAAR03A01M4G_BETA_20200107F 1 [Dec 30 2019 17:00:00]
[04-02_04:12:18:539] qmap_settings.rx_urb_size = 16384
[04-02_04:12:18:539] qmap_settings.ul_data_aggregation_max_datagrams = 16
[04-02_04:12:18:539] qmap_settings.ul_data_aggregation_max_size = 8192
[04-02_04:12:18:539] qmap_settings.dl_minimum_padding = 0
[04-02_04:12:18:550] requestSetLoopBackState(loopback_state=1, replication_factor=14)
[04-02_04:12:18:557] requestGetSIMStatus SIMStatus: SIM_ABSENT
[04-02_04:12:18:560] requestGetProfile[1] ///0
[04-02_04:12:18:563] requestRegistrationState2 MCC: 0, MNC: 0, PS: Detached, DataCap: UNKNOW
[04-02_04:12:18:565] requestQueryDataCall IPv4ConnectionStatus: DISCONNECTED
[04-02_04:12:18:566] ifconfig rmnet_mhi0.1 down
[04-02_04:12:18:571] ifconfig rmnet_mhi0.1 0.0.0.0
ifconfig: SIOCSIFFLAGS: Network is down
[04-02_04:12:18:575] SetLoopBackInd: loopback_state=1, replication_factor=14
[04-02_04:12:18:591] requestSetupDataCall WdsConnectionIPv4Handle: 0xe40182a0
[04-02_04:12:18:601] ifconfig rmnet_mhi0 up
[04-02_04:12:18:607] ifconfig rmnet_mhi0.1 up
[04-02_04:12:18:613] you are use OpenWrt?
[04-02_04:12:18:614] should not calling udhcpc manually?
[04-02_04:12:18:614] should modify /etc/config/network as below?
[04-02_04:12:18:614] config interface wan
[04-02_04:12:18:614] option ifname rmnet_mhi0.1
[04-02_04:12:18:614] option proto dhcp
[04-02_04:12:18:614] should use "/sbin/ifstaus wan" to check rmnet_mhi0.1 's status?
[04-02_04:12:18:614] busybox udhcpc -f -n -q -t 5 -i rmnet_mhi0.1
udhcpc: started, v1.28.3
udhcpc: sending discover
udhcpc: sending select for 192.168.48.171
udhcpc: lease of 192.168.48.171 obtained, lease time 7200
[04-02_04:12:18:809] udhcpc: ifconfig rmnet_mhi0.1 192.168.48.171 netmask 255.255.255.248 broadcast +
[04-02_04:12:18:819] udhcpc: setting default routers: 192.168.48.172
root@OpenWrt:~# ifconfig rmnet_mhi0
rmnet_mhi0 Link encap:Ethernet HWaddr 02:50:F4:00:00:00
inet6 addr: fe80::50:f4ff:fe00:0/64 Scope:Link
UP RUNNING NOARP MTU:1500 Metric:1
RX packets:2 errors:0 dropped:0 overruns:0 frame:0
TX packets:2 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:608 (608.0 B) TX bytes:672 (672.0 B)
root@OpenWrt:~# ifconfig rmnet_mhi0.1
rmnet_mhi0.1 Link encap:UNSPEC HWaddr 02-50-F4-00-00-00-00-00-00-00-00-00-00-00-00-00
inet addr:192.168.48.171 Mask:255.255.255.248
inet6 addr: fe80::50:f4ff:fe00:0/64 Scope:Link
UP RUNNING NOARP MTU:1500 Metric:1
RX packets:2 errors:0 dropped:0 overruns:0 frame:0
TX packets:2 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:592 (592.0 B) TX bytes:656 (656.0 B)
# adjust CPU load balancing
root@OpenWrt:~# echo 2 > /sys/class/net/rmnet_mhi0/queues/rx-0/rps_cpus
root@OpenWrt:~# echo 4 > /sys/class/net/rmnet_mhi0.1/queues/rx-0/rps_cpus
root@OpenWrt:~# echo 2000 > /proc/sys/net/core/netdev_max_backlog
root@OpenWrt:~# cat /sys/class/net/rmnet_mhi0/queues/rx-0/rps_cpus
2
root@OpenWrt:~# cat /sys/class/net/rmnet_mhi0.1/queues/rx-0/rps_cpus
4
root@OpenWrt:~# cat /proc/sys/net/core/netdev_max_backlog
2000
-14
View File
@@ -1,14 +0,0 @@
root@imx6qsabresd:~# ./QLog -p /dev/mhi_DIAG -s log &
root@imx6qsabresd:~# [000.000]QLog Version: Quectel_QLog_Linux&Android_V1.2.4
[ 298.597963] [I][mhi_uci_open] Node open, ref counts 1
[ 298.605601] [I][mhi_uci_open] Starting channel
[ 298.612159] [I][__mhi_prepare_channel] Entered: preparing channel:4
[ 298.629906] [I][mhi_dump_tre] carl_ev evt_cmd_comp code=1
[ 298.635415] [I][__mhi_prepare_channel] Chan:4 successfully moved to start state
[ 298.642749] [I][__mhi_prepare_channel] Entered: preparing channel:5
[ 298.658043] [I][mhi_dump_tre] carl_ev evt_cmd_comp code=1
[ 298.663543] [I][__mhi_prepare_channel] Chan:5 successfully moved to start state
[000.075]open /dev/mhi_DIAG ttyfd = 3
[000.075]Press CTRL+C to stop catch log.
[000.096]qlog_logfile_create log/20160920_145758_0000.qmdl logfd=4
[005.268]recv: 0M 70K 490B in 5181 msec
-47
View File
@@ -1,47 +0,0 @@
#
# 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_q
PKG_VERSION:=3.0
PKG_RELEASE:=2
include $(INCLUDE_DIR)/kernel.mk
include $(INCLUDE_DIR)/package.mk
define KernelPackage/qmi_wwan_q
SUBMENU:=WWAN Support
TITLE:=Quectel Linux USB QMI WWAN Driver
DEPENDS:=+kmod-usb-net +kmod-usb-wdm
FILES:=$(PKG_BUILD_DIR)/qmi_wwan_q.ko
AUTOLOAD:=$(call AutoLoad,81,qmi_wwan_q)
endef
define KernelPackage/qmi_wwan_q/description
Quectel 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_q))
-36
View File
@@ -1,36 +0,0 @@
obj-m += qmi_wwan_q.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_q.ko /lib/modules/$(shell uname -r)/kernel/drivers/net/usb/
depmod
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
-146
View File
@@ -1,146 +0,0 @@
Release Notes
[V1.2.2]
Date: 9/7/2022
enhancement:
1. Optimization, the network card send queue wakeup is changed from callback to tasklet
2. Add the function of returning LAN packets in bridge mode
3. support ndo ioctl on kernel>5.14
4. Allow setting MTU greater than 1500
fix:
[V1.2.1]
Date: 9/26/2021
enhancement:
1. support IPQ5018's NSS
2. use 'qsdk/qca/src/data-kernel/drivers/rmnet-nss/rmnet_nss.c' instead myself rmnet_nss.c
and qmi_wwan_q.ko must load after rmnet_nss.ko
fix:
[V1.2.0.25]
Date: 9/17/2021
enhancement:
fix:
1. add sdx6x platfrom support
[V1.2.0.24]
Date: 9/6/2021
enhancement:
fix:
1. add BG95 support
2. support Linux 5.14.0
[V1.2.0.23]
Date: 3/23/2021
enhancement:
fix:
1. add sdx12 platfrom support
[V1.2.0.22]
Date: 2/5/2021
enhancement:
fix:
1. fix panic (memory-access-after-free) when do modem reboot stress test
[V1.2.0.21]
Date: 2/4/2021
enhancement:
1. Code refactoring - QMAP and rmnet
fix:
1. qmap_qmi_wwan_rx_fixup: change skb_dequeue to __skb_dequeue
[V1.2.0.20]
Date: 11/2/2020
enhancement:
fix:
1. LTE-A modems can not obtain IP by DHCP
[V1.2.0.19]
Date: 10/9/2020
enhancement:
fix:
1. X55 can not access internet after usb resume
[V1.2.0.18]
Date: 10/9/2020
enhancement:
fix:
1. X55: rename rmnet_usb0.1 to wwan0_1
1.1 if there is '.', openwrt will think it is vlan, and auto create vlan
1.2 if there is '.', android will think it is not vaild
1.3 if named as rmnet_usb0, and SOC is QCOM, QCOM's netmgr will auto manager it
[V1.2.0.17]
Date: 9/14/2020
enhancement:
1. Code refactoring - QMAP size and version
fix:
[V1.2.0.16]
Date: 9/14/2020
enhancement:
1. rx_fixup() check if this is enough skb_headroom() to fill ethernet header
fix:
1. fix "WARNING: suspicious RCU usage"
[V1.2.0.15]
Date: 9/10/2020
enhancement:
fix:
1. fix compile errors on kernel 3.10~3.13
[V1.2.0.14]
Date: 7/24/2020
enhancement:
fix:
1. fix QMAP V5 bug on Big Endian CPU
[V1.2.0.13]
Date: 6/22/2020
enhancement:
fix:
1. fix no data traffic when do Upload TPUT test
[V1.2.0.12]
Date: 5/29/2020
enhancement:
fix:
1. IPQ8074: when enable hyfi, quectel-CM will crash system crash
[V1.2.0.9]
Date: 5/13/2020
enhancement:
fix:
1. IPQ8074: enable CONFIG_QCA_NSS_DRV by CONFIG_PINCTRL_IPQ807x (from CONFIG_ARCH_IPQ807x)
[V1.2.0.8]
Date: 5/9/2020
enhancement:
fix:
1. fix compile errors on kernel V3.10
[V1.2.0.7]
Date: 4/25/2020
enhancement:
1. X55 support bridge mode
fix:
[V1.2.0.6]
Date: 4/20/2020
enhancement:
1. add stat64, or the rx/tx statics will become to 0 when data > 4G
2. do not use skb_clone, will make QCOM's NSS and SFE 's cpu loading very high
fix:
[V1.2.0.5]
Date: 4/8/2020
enhancement:
1. add attrite link_state, change carrier state accoring link_state
quectel-CM will set link_state to 1 when QMI setup call success.
fix:
[V1.2.0.4]
Date: 4/8/2020
enhancement:
1. support X55's QMAP V5
fix:
File diff suppressed because it is too large. Load diff
-424
View File
@@ -1,424 +0,0 @@
/* Copyright (c) 2019, 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/netdevice.h>
#include <linux/skbuff.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/hashtable.h>
#include <linux/if_ether.h>
#include <linux/ip.h>
#include <qca-nss-drv/nss_api_if.h>
#include <linux/rmnet_nss.h>
#define RMNET_NSS_HASH_BITS 8
#define hash_add_ptr(table, node, key) \
hlist_add_head(node, &table[hash_ptr(key, HASH_BITS(table))])
static DEFINE_HASHTABLE(rmnet_nss_ctx_hashtable, RMNET_NSS_HASH_BITS);
struct rmnet_nss_ctx {
struct hlist_node hnode;
struct net_device *rmnet_dev;
struct nss_rmnet_rx_handle *nss_ctx;
};
enum __rmnet_nss_stat {
RMNET_NSS_RX_ETH,
RMNET_NSS_RX_FAIL,
RMNET_NSS_RX_NON_ETH,
RMNET_NSS_RX_BUSY,
RMNET_NSS_TX_NO_CTX,
RMNET_NSS_TX_SUCCESS,
RMNET_NSS_TX_FAIL,
RMNET_NSS_TX_NONLINEAR,
RMNET_NSS_TX_BAD_IP,
RMNET_NSS_EXCEPTIONS,
RMNET_NSS_EX_BAD_HDR,
RMNET_NSS_EX_BAD_IP,
RMNET_NSS_EX_SUCCESS,
RMNET_NSS_TX_BAD_FRAGS,
RMNET_NSS_TX_LINEARIZE_FAILS,
RMNET_NSS_TX_NON_ZERO_HEADLEN_FRAGS,
RMNET_NSS_TX_BUSY_LOOP,
RMNET_NSS_NUM_STATS,
};
static unsigned long rmnet_nss_stats[RMNET_NSS_NUM_STATS];
#define RMNET_NSS_STAT(name, counter, desc) \
module_param_named(name, rmnet_nss_stats[counter], ulong, 0444); \
MODULE_PARM_DESC(name, desc)
RMNET_NSS_STAT(rmnet_nss_rx_ethernet, RMNET_NSS_RX_ETH,
"Number of Ethernet headers successfully removed");
RMNET_NSS_STAT(rmnet_nss_rx_fail, RMNET_NSS_RX_FAIL,
"Number of Ethernet headers that could not be removed");
RMNET_NSS_STAT(rmnet_nss_rx_non_ethernet, RMNET_NSS_RX_NON_ETH,
"Number of non-Ethernet packets received");
RMNET_NSS_STAT(rmnet_nss_rx_busy, RMNET_NSS_RX_BUSY,
"Number of packets dropped decause rmnet_data device was busy");
RMNET_NSS_STAT(rmnet_nss_tx_slow, RMNET_NSS_TX_NO_CTX,
"Number of packets sent over non-NSS-accelerated rmnet device");
RMNET_NSS_STAT(rmnet_nss_tx_fast, RMNET_NSS_TX_SUCCESS,
"Number of packets sent over NSS-accelerated rmnet device");
RMNET_NSS_STAT(rmnet_nss_tx_fail, RMNET_NSS_TX_FAIL,
"Number of packets that NSS could not transmit");
RMNET_NSS_STAT(rmnet_nss_tx_nonlinear, RMNET_NSS_TX_NONLINEAR,
"Number of non linear sent over NSS-accelerated rmnet device");
RMNET_NSS_STAT(rmnet_nss_tx_invalid_ip, RMNET_NSS_TX_BAD_IP,
"Number of ingress packets with invalid IP headers");
RMNET_NSS_STAT(rmnet_nss_tx_invalid_frags, RMNET_NSS_TX_BAD_FRAGS,
"Number of ingress packets with invalid frag format");
RMNET_NSS_STAT(rmnet_nss_tx_linearize_fail, RMNET_NSS_TX_LINEARIZE_FAILS,
"Number of ingress packets where linearize in tx fails");
RMNET_NSS_STAT(rmnet_nss_tx_exceptions, RMNET_NSS_EXCEPTIONS,
"Number of times our DL exception handler was invoked");
RMNET_NSS_STAT(rmnet_nss_exception_non_ethernet, RMNET_NSS_EX_BAD_HDR,
"Number of non-Ethernet exception packets");
RMNET_NSS_STAT(rmnet_nss_exception_invalid_ip, RMNET_NSS_EX_BAD_IP,
"Number of exception packets with invalid IP headers");
RMNET_NSS_STAT(rmnet_nss_exception_success, RMNET_NSS_EX_SUCCESS,
"Number of exception packets handled successfully");
RMNET_NSS_STAT(rmnet_nss_tx_non_zero_headlen_frags, RMNET_NSS_TX_NON_ZERO_HEADLEN_FRAGS,
"Number of packets with non zero headlen");
RMNET_NSS_STAT(rmnet_nss_tx_busy_loop, RMNET_NSS_TX_BUSY_LOOP,
"Number of times tx packets busy looped");
static void rmnet_nss_inc_stat(enum __rmnet_nss_stat stat)
{
if (stat >= 0 && stat < RMNET_NSS_NUM_STATS)
rmnet_nss_stats[stat]++;
}
static struct rmnet_nss_ctx *rmnet_nss_find_ctx(struct net_device *dev)
{
struct rmnet_nss_ctx *ctx;
struct hlist_head *bucket;
u32 hash;
hash = hash_ptr(dev, HASH_BITS(rmnet_nss_ctx_hashtable));
bucket = &rmnet_nss_ctx_hashtable[hash];
hlist_for_each_entry(ctx, bucket, hnode) {
if (ctx->rmnet_dev == dev)
return ctx;
}
return NULL;
}
static void rmnet_nss_free_ctx(struct rmnet_nss_ctx *ctx)
{
if (ctx) {
hash_del(&ctx->hnode);
nss_rmnet_rx_xmit_callback_unregister(ctx->nss_ctx);
nss_rmnet_rx_destroy_sync(ctx->nss_ctx);
kfree(ctx);
}
}
/* Pull off an ethernet header, if possible */
static int rmnet_nss_ethhdr_pull(struct sk_buff *skb)
{
if (!skb->protocol || skb->protocol == htons(ETH_P_802_3)) {
void *ret = skb_pull(skb, sizeof(struct ethhdr));
rmnet_nss_inc_stat((ret) ? RMNET_NSS_RX_ETH :
RMNET_NSS_RX_FAIL);
return !ret;
}
rmnet_nss_inc_stat(RMNET_NSS_RX_NON_ETH);
return -1;
}
/* Copy headers to linear section for non linear packets */
static int rmnet_nss_adjust_header(struct sk_buff *skb)
{
struct iphdr *iph;
skb_frag_t *frag;
int bytes = 0;
u8 transport;
if (skb_shinfo(skb)->nr_frags != 1) {
rmnet_nss_inc_stat(RMNET_NSS_TX_BAD_FRAGS);
return -EINVAL;
}
if (skb_headlen(skb)) {
rmnet_nss_inc_stat(RMNET_NSS_TX_NON_ZERO_HEADLEN_FRAGS);
return 0;
}
frag = &skb_shinfo(skb)->frags[0];
iph = (struct iphdr *)(skb_frag_address(frag));
if (iph->version == 4) {
bytes = iph->ihl*4;
transport = iph->protocol;
} else if (iph->version == 6) {
struct ipv6hdr *ip6h = (struct ipv6hdr *)iph;
bytes = sizeof(struct ipv6hdr);
/* Dont have to account for extension headers yet */
transport = ip6h->nexthdr;
} else {
rmnet_nss_inc_stat(RMNET_NSS_TX_BAD_IP);
return -EINVAL;
}
if (transport == IPPROTO_TCP) {
struct tcphdr *th;
th = (struct tcphdr *)((u8 *)iph + bytes);
bytes += th->doff * 4;
} else if (transport == IPPROTO_UDP) {
bytes += sizeof(struct udphdr);
} else {
/* cant do anything else here unfortunately so linearize */
if (skb_linearize(skb)) {
rmnet_nss_inc_stat(RMNET_NSS_TX_LINEARIZE_FAILS);
return -EINVAL;
} else {
return 0;
}
}
if (bytes > skb_frag_size(frag)) {
rmnet_nss_inc_stat(RMNET_NSS_TX_BAD_FRAGS);
return -EINVAL;
}
skb_push(skb, bytes);
memcpy(skb->data, iph, bytes);
/* subtract to account for skb_push */
skb->len -= bytes;
frag->page_offset += bytes;
skb_frag_size_sub(frag, bytes);
/* subtract to account for skb_frag_size_sub */
skb->data_len -= bytes;
return 0;
}
/* Main downlink handler
* Looks up NSS contex associated with the device. If the context is found,
* we add a dummy ethernet header with the approriate protocol field set,
* the pass the packet off to NSS for hardware acceleration.
*/
int rmnet_nss_tx(struct sk_buff *skb)
{
struct ethhdr *eth;
struct rmnet_nss_ctx *ctx;
struct net_device *dev = skb->dev;
nss_tx_status_t rc;
unsigned int len;
u8 version;
if (skb_is_nonlinear(skb)) {
if (rmnet_nss_adjust_header(skb))
goto fail;
else
rmnet_nss_inc_stat(RMNET_NSS_TX_NONLINEAR);
}
version = ((struct iphdr *)skb->data)->version;
ctx = rmnet_nss_find_ctx(dev);
if (!ctx) {
rmnet_nss_inc_stat(RMNET_NSS_TX_NO_CTX);
return -EINVAL;
}
eth = (struct ethhdr *)skb_push(skb, sizeof(*eth));
memset(&eth->h_dest, 0, ETH_ALEN * 2);
if (version == 4) {
eth->h_proto = htons(ETH_P_IP);
} else if (version == 6) {
eth->h_proto = htons(ETH_P_IPV6);
} else {
rmnet_nss_inc_stat(RMNET_NSS_TX_BAD_IP);
goto fail;
}
skb->protocol = htons(ETH_P_802_3);
/* Get length including ethhdr */
len = skb->len;
transmit:
rc = nss_rmnet_rx_tx_buf(ctx->nss_ctx, skb);
if (rc == NSS_TX_SUCCESS) {
/* Increment rmnet_data device stats.
* Don't call rmnet_data_vnd_rx_fixup() to do this, as
* there's no guarantee the skb pointer is still valid.
*/
dev->stats.rx_packets++;
dev->stats.rx_bytes += len;
rmnet_nss_inc_stat(RMNET_NSS_TX_SUCCESS);
return 0;
} else if (rc == NSS_TX_FAILURE_QUEUE) {
rmnet_nss_inc_stat(RMNET_NSS_TX_BUSY_LOOP);
goto transmit;
}
fail:
rmnet_nss_inc_stat(RMNET_NSS_TX_FAIL);
kfree_skb(skb);
return 1;
}
/* Called by NSS in the DL exception case.
* Since the packet cannot be sent over the accelerated path, we need to
* handle it. Remove the ethernet header and pass it onward to the stack
* if possible.
*/
void rmnet_nss_receive(struct net_device *dev, struct sk_buff *skb,
struct napi_struct *napi)
{
rmnet_nss_inc_stat(RMNET_NSS_EXCEPTIONS);
if (!skb)
return;
if (rmnet_nss_ethhdr_pull(skb)) {
rmnet_nss_inc_stat(RMNET_NSS_EX_BAD_HDR);
goto drop;
}
/* reset header pointers */
skb_reset_transport_header(skb);
skb_reset_network_header(skb);
skb_reset_mac_header(skb);
/* reset packet type */
skb->pkt_type = PACKET_HOST;
skb->dev = dev;
/* reset protocol type */
switch (skb->data[0] & 0xF0) {
case 0x40:
skb->protocol = htons(ETH_P_IP);
break;
case 0x60:
skb->protocol = htons(ETH_P_IPV6);
break;
default:
rmnet_nss_inc_stat(RMNET_NSS_EX_BAD_IP);
goto drop;
}
rmnet_nss_inc_stat(RMNET_NSS_EX_SUCCESS);
/* Set this so that we dont loop around netif_receive_skb */
skb->cb[0] = 1;
netif_receive_skb(skb);
return;
drop:
kfree_skb(skb);
}
/* Called by NSS in the UL acceleration case.
* We are guaranteed to have an ethernet packet here from the NSS hardware,
* We need to pull the header off and invoke our ndo_start_xmit function
* to handle transmitting the packet to the network stack.
*/
void rmnet_nss_xmit(struct net_device *dev, struct sk_buff *skb)
{
netdev_tx_t ret;
skb_pull(skb, sizeof(struct ethhdr));
rmnet_nss_inc_stat(RMNET_NSS_RX_ETH);
/* NSS takes care of shaping, so bypassing Qdiscs like this is OK */
ret = dev->netdev_ops->ndo_start_xmit(skb, dev);
if (unlikely(ret == NETDEV_TX_BUSY)) {
dev_kfree_skb_any(skb);
rmnet_nss_inc_stat(RMNET_NSS_RX_BUSY);
}
}
/* Create and register an NSS context for an rmnet_data device */
int rmnet_nss_create_vnd(struct net_device *dev)
{
struct rmnet_nss_ctx *ctx;
ctx = kzalloc(sizeof(*ctx), GFP_ATOMIC);
if (!ctx)
return -ENOMEM;
ctx->rmnet_dev = dev;
ctx->nss_ctx = nss_rmnet_rx_create_sync_nexthop(dev, NSS_N2H_INTERFACE,
NSS_C2C_TX_INTERFACE);
if (!ctx->nss_ctx) {
kfree(ctx);
return -1;
}
nss_rmnet_rx_register(ctx->nss_ctx, rmnet_nss_receive, dev);
nss_rmnet_rx_xmit_callback_register(ctx->nss_ctx, rmnet_nss_xmit);
hash_add_ptr(rmnet_nss_ctx_hashtable, &ctx->hnode, dev);
return 0;
}
/* Unregister and destroy the NSS context for an rmnet_data device */
int rmnet_nss_free_vnd(struct net_device *dev)
{
struct rmnet_nss_ctx *ctx;
ctx = rmnet_nss_find_ctx(dev);
rmnet_nss_free_ctx(ctx);
return 0;
}
static const struct rmnet_nss_cb rmnet_nss = {
.nss_create = rmnet_nss_create_vnd,
.nss_free = rmnet_nss_free_vnd,
.nss_tx = rmnet_nss_tx,
};
int __init rmnet_nss_init(void)
{
pr_err("%s(): initializing rmnet_nss\n", __func__);
RCU_INIT_POINTER(rmnet_nss_callbacks, &rmnet_nss);
return 0;
}
void __exit rmnet_nss_exit(void)
{
struct hlist_node *tmp;
struct rmnet_nss_ctx *ctx;
int bkt;
pr_err("%s(): exiting rmnet_nss\n", __func__);
RCU_INIT_POINTER(rmnet_nss_callbacks, NULL);
/* Tear down all NSS contexts */
hash_for_each_safe(rmnet_nss_ctx_hashtable, bkt, tmp, ctx, hnode)
rmnet_nss_free_ctx(ctx);
}
#if 0
MODULE_LICENSE("GPL v2");
module_init(rmnet_nss_init);
module_exit(rmnet_nss_exit);
#endif