HYL_OK3568_LINUX/app/forlinx/quectelCM/quectel-mbim-proxy.c
2025-05-10 21:49:39 +08:00

438 lines
13 KiB
C

#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <stdarg.h>
#include <stddef.h>
#include <fcntl.h>
#include <pthread.h>
#include <poll.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/ioctl.h>
#include <linux/un.h>
#include <linux/in.h>
#include <linux/if.h>
#include <dirent.h>
#include <signal.h>
#include <endian.h>
#include <inttypes.h>
#include <getopt.h>
#define QUECTEL_MBIM_PROXY "quectel-mbim-proxy"
#define safe_close(_fd) do { if (_fd > 0) { close(_fd); _fd = -1; } } while(0)
#define CM_MAX_CLIENT 32
#define TID_MASK (0xFFFFFF)
#define TID_SHIFT (24)
typedef enum {
MBIM_OPEN_MSG = 1,
MBIM_CLOSE_MSG = 2,
MBIM_OPEN_DONE = 0x80000001,
MBIM_CLOSE_DONE = 0x80000002,
} MBIM_MSG;
typedef struct {
unsigned int MessageType;
unsigned int MessageLength;
unsigned int TransactionId;
} MBIM_MESSAGE_HEADER;
typedef struct {
MBIM_MESSAGE_HEADER MessageHeader;
unsigned int MaxControlTransfer;
} MBIM_OPEN_MSG_T;
typedef struct {
MBIM_MESSAGE_HEADER MessageHeader;
unsigned int Status;
} MBIM_OPEN_DONE_T;
typedef struct {
int client_fd;
int client_idx;
} CM_CLIENT_T;
static unsigned char cm_recv_buffer[4096];
static CM_CLIENT_T cm_clients[CM_MAX_CLIENT];
static int verbose = 0;
const char * get_time(void) {
static char time_buf[128];
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;
}
#define mbim_debug(fmt, args...) do { fprintf(stdout, "%s " fmt, get_time(), ##args); } while(0);
static int non_block_write(int fd, void *data, int len)
{
int ret;
struct pollfd pollfd = {fd, POLLOUT, 0};
ret = poll(&pollfd, 1, 3000);
if (ret <= 0) {
mbim_debug("%s poll ret=%d, errno: %d(%s)\n", __func__, ret, errno, strerror(errno));
}
ret = write (fd, data, len);
if (ret != len)
mbim_debug("%s write ret=%d, errno: %d(%s)\n", __func__, ret, errno, strerror(errno));
return len;
}
static int mbim_send_open_msg(int mbim_dev_fd, uint32_t MaxControlTransfer) {
MBIM_OPEN_MSG_T open_msg;
MBIM_OPEN_MSG_T *pRequest = &open_msg;
pRequest->MessageHeader.MessageType = (MBIM_OPEN_MSG);
pRequest->MessageHeader.MessageLength = (sizeof(MBIM_OPEN_MSG_T));
pRequest->MessageHeader.TransactionId = (1);
pRequest->MaxControlTransfer = (MaxControlTransfer);
mbim_debug("%s()\n", __func__);
return non_block_write(mbim_dev_fd, pRequest, sizeof(MBIM_OPEN_MSG_T));
}
/*
* parameter: proxy name
* return: local proxy server fd or -1
*/
static int proxy_make_server(const char *proxy_name)
{
int len, flag;
struct sockaddr_un sockaddr;
int mbim_server_fd;
mbim_server_fd = socket(AF_LOCAL, SOCK_STREAM, 0);
if (mbim_server_fd < 0) {
mbim_debug("socket failed: %s\n", strerror(errno));
return -1;
}
if (fcntl(mbim_server_fd, F_SETFL, fcntl(mbim_server_fd, F_GETFL) | O_NONBLOCK) < 0)
mbim_debug("fcntl set server(%d) NONBLOCK attribute failed: %s\n", mbim_server_fd, strerror(errno));
memset(&sockaddr, 0, sizeof(sockaddr));
sockaddr.sun_family = AF_LOCAL;
sockaddr.sun_path[0] = 0;
snprintf(sockaddr.sun_path, UNIX_PATH_MAX, "0%s", proxy_name);
sockaddr.sun_path[0] = '\0'; // string starts with leading '\0'
flag = 1;
if (setsockopt(mbim_server_fd, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof(flag)) < 0) {
safe_close(mbim_server_fd);
mbim_debug("setsockopt failed\n");
}
len = strlen(proxy_name) + offsetof(struct sockaddr_un, sun_path) + 1;
if (bind(mbim_server_fd, (struct sockaddr*)&sockaddr, len) < 0) {
safe_close(mbim_server_fd);
mbim_debug("bind failed: %s\n", strerror(errno));
return -1;
}
listen(mbim_server_fd, 4);
return mbim_server_fd;
}
static int handle_client_connect(int server_fd)
{
int i, client_fd;
struct sockaddr_in cli_addr;
socklen_t len = sizeof(cli_addr);
client_fd = accept(server_fd, (struct sockaddr *)&cli_addr, &len);
if (client_fd < 0) {
mbim_debug("proxy accept failed: %s\n", strerror(errno));
return -1;
}
if (fcntl(client_fd, F_SETFL, fcntl(client_fd, F_GETFL) | O_NONBLOCK) < 0)
mbim_debug("fcntl set client(%d) NONBLOCK attribute failed: %s\n", client_fd, strerror(errno));
for (i = 0; i < CM_MAX_CLIENT; i++) {
if (cm_clients[i].client_fd <= 0) {
cm_clients[i].client_fd = client_fd;
cm_clients[i].client_idx= i+1;
mbim_debug("%s client_fd=%d, client_idx=%d\n", __func__, cm_clients[i].client_fd, cm_clients[i].client_idx);
return 0;
}
}
close(client_fd);
return -1;
}
static void handle_client_disconnect(int client_fd)
{
int i;
for (i = 0; i < CM_MAX_CLIENT; i++) {
if (cm_clients[i].client_fd == client_fd) {
mbim_debug("%s client_fd=%d, client_idx=%d\n", __func__, cm_clients[i].client_fd, cm_clients[i].client_idx);
safe_close(cm_clients[i].client_fd);
return;
}
}
}
static int handle_client_request(int mbim_dev_fd, int client_fd, void *pdata, int len)
{
int i;
int client_idx = -1;
int ret;
MBIM_MESSAGE_HEADER *pRequest = (MBIM_MESSAGE_HEADER *)pdata;
for (i = 0; i < CM_MAX_CLIENT; i++) {
if (cm_clients[i].client_fd == client_fd) {
client_idx = cm_clients[i].client_idx;
break;
}
}
if (client_idx == -1) {
goto error;
}
/* transfer TransicationID to proxy transicationID and record in sender list */
pRequest->TransactionId = (pRequest->TransactionId & TID_MASK) + (client_idx << TID_SHIFT);
if (verbose) mbim_debug("REQ client_fd=%d, client_idx=%d, tid=%u\n", cm_clients[i].client_fd, cm_clients[i].client_idx, (pRequest->TransactionId & TID_MASK));
ret = non_block_write (mbim_dev_fd, pRequest, len);
if (ret == len)
return 0;
error:
return -1;
}
/*
* Will read message from device and transfer it to clients/client
* Notice:
* unsocial message will be send to all clients
*/
static int handle_device_response(void *pdata, int len)
{
int i;
MBIM_MESSAGE_HEADER *pResponse = (MBIM_MESSAGE_HEADER *)pdata;
/* unsocial/function error message */
if (pResponse->TransactionId == 0) {
for (i = 0; i < CM_MAX_CLIENT; i++) {
if (cm_clients[i].client_fd > 0) {
non_block_write(cm_clients[i].client_fd, pResponse, len);
}
}
}
else {
/* try to find the sender */
int client_idx = (pResponse->TransactionId >> TID_SHIFT);
for (i = 0; i < CM_MAX_CLIENT; i++) {
if (cm_clients[i].client_idx == client_idx && cm_clients[i].client_fd > 0) {
pResponse->TransactionId &= TID_MASK;
if (verbose) mbim_debug("RSP client_fd=%d, client_idx=%d, tid=%u\n", cm_clients[i].client_fd, cm_clients[i].client_idx, (pResponse->TransactionId & TID_MASK));
non_block_write(cm_clients[i].client_fd, pResponse, len);
break;
}
}
if ( i == CM_MAX_CLIENT) {
mbim_debug("%s nobody care tid=%u\n", __func__, pResponse->TransactionId);
}
}
return 0;
}
static int proxy_loop(int mbim_dev_fd)
{
int i;
int mbim_server_fd = -1;
while (mbim_dev_fd > 0) {
struct pollfd pollfds[2+CM_MAX_CLIENT];
int ne, ret, nevents = 0;
pollfds[nevents].fd = mbim_dev_fd;
pollfds[nevents].events = POLLIN;
pollfds[nevents].revents= 0;
nevents++;
if (mbim_server_fd > 0) {
pollfds[nevents].fd = mbim_server_fd;
pollfds[nevents].events = POLLIN;
pollfds[nevents].revents= 0;
nevents++;
for (i = 0; i < CM_MAX_CLIENT; i++) {
if (cm_clients[i].client_fd > 0) {
pollfds[nevents].fd = cm_clients[i].client_fd;
pollfds[nevents].events = POLLIN;
pollfds[nevents].revents= 0;
nevents++;
}
}
}
ret = poll(pollfds, nevents, (mbim_server_fd > 0) ? -1 : (10*1000));
if (ret <= 0) {
goto error;
}
for (ne = 0; ne < nevents; ne++) {
int fd = pollfds[ne].fd;
short revents = pollfds[ne].revents;
if (revents & (POLLERR | POLLHUP | POLLNVAL)) {
mbim_debug("%s poll fd = %d, revents = %04x\n", __func__, fd, revents);
if (fd == mbim_dev_fd) {
goto error;
} else if(fd == mbim_server_fd) {
} else {
handle_client_disconnect(fd);
}
continue;
}
if (!(pollfds[ne].revents & POLLIN)) {
continue;
}
if (fd == mbim_server_fd) {
handle_client_connect(fd);
}
else {
int len = read(fd, cm_recv_buffer, sizeof(cm_recv_buffer));
if (len <= 0) {
mbim_debug("%s read fd=%d, len=%d, errno: %d(%s)\n", __func__, fd, len, errno, strerror(errno));
if (fd == mbim_dev_fd)
goto error;
else
handle_client_disconnect(fd);
return len;
}
if (fd == mbim_dev_fd) {
if (mbim_server_fd == -1) {
MBIM_OPEN_DONE_T *pOpenDone = (MBIM_OPEN_DONE_T *)cm_recv_buffer;
if (pOpenDone->MessageHeader.MessageType == MBIM_OPEN_DONE) {
mbim_debug("receive MBIM_OPEN_DONE, status=%d\n", pOpenDone->Status);
if (pOpenDone->Status)
goto error;
mbim_server_fd = proxy_make_server(QUECTEL_MBIM_PROXY);
mbim_debug("mbim_server_fd=%d\n", mbim_server_fd);
}
}
else {
handle_device_response(cm_recv_buffer, len);
}
}
else {
handle_client_request(mbim_dev_fd, fd, cm_recv_buffer, len);
}
}
}
}
error:
safe_close(mbim_server_fd);
for (i = 0; i < CM_MAX_CLIENT; i++) {
safe_close(cm_clients[i].client_fd);
}
mbim_debug("%s exit\n", __func__);
return 0;
}
/*
* How to use this proxy?
* 1. modprobe -a 8021q
* 2. Create network interface for channels:
* ip link add link wwan0 name wwan0.1 type vlan id 1
* ip link add link wwan0 name wwan0.2 type vlan id 2
* 3. Start './mbim-proxy' with -d 'device'
* 4. Start Clients: ./quectel-CM -n id1
* 5. Start Clients: ./quectel-CM -n id2
* ...
* Notice:
* mbim-proxy can work in backgroud as a daemon
* '-n' sessionID
* The modem may not support multi-PDN mode or how many PDN it supports is undefined. It depends!!!
* Besides, some modem also may not support some sessionID. For instance EC20 doesn't support SessionId 1...
*/
int main(int argc, char **argv)
{
int optidx = 0;
int opt;
char *optstr = "d:vh";
const char *device = "/dev/cdc-wdm0";
struct option options[] = {
{"verbose", no_argument, NULL, 'v'},
{"device", required_argument, NULL, 'd'},
{0, 0, 0, 0},
};
while ((opt = getopt_long(argc, argv, optstr, options, &optidx)) != -1) {
switch (opt) {
case 'v':
verbose = 1;
break;
case 'd':
device = optarg;
break;
case 'h':
mbim_debug("-h Show this message\n");
mbim_debug("-v Verbose\n");
mbim_debug("-d [device] MBIM device\n");
return 0;
default:
mbim_debug("illegal argument\n");
return -1;
}
}
if (!device) {
mbim_debug("Missing parameter: device\n");
return -1;
}
while (1) {
int mbim_dev_fd = open(device, O_RDWR | O_NONBLOCK | O_NOCTTY);
if (mbim_dev_fd < 0) {
mbim_debug("cannot open mbim_device %s: %s\n", device, strerror(errno));
sleep(2);
continue;
}
mbim_debug ("mbim_dev_fd=%d\n", mbim_dev_fd);
memset(cm_clients, 0, sizeof(cm_clients));
mbim_send_open_msg(mbim_dev_fd, sizeof(cm_recv_buffer));
proxy_loop(mbim_dev_fd);
safe_close(mbim_dev_fd);
}
return -1;
}