1081 lines
36 KiB
C
Executable File
1081 lines
36 KiB
C
Executable File
#include "QMIThread.h"
|
|
#include <sys/wait.h>
|
|
#include <sys/utsname.h>
|
|
#include <sys/time.h>
|
|
#include <dirent.h>
|
|
|
|
//#define CONFIG_EXIT_WHEN_DIAL_FAILED
|
|
//#define CONFIG_BACKGROUND_WHEN_GET_IP
|
|
//#define CONFIG_PID_FILE_FORMAT "/var/run/quectel-CM-%s.pid" //for example /var/run/quectel-CM-wwan0.pid
|
|
|
|
int debug_qmi = 0;
|
|
int main_loop = 0;
|
|
char * qmichannel;
|
|
int qmidevice_control_fd[2];
|
|
static int signal_control_fd[2];
|
|
|
|
#ifdef CONFIG_BACKGROUND_WHEN_GET_IP
|
|
static int daemon_pipe_fd[2];
|
|
|
|
static void ql_prepare_daemon(void) {
|
|
pid_t daemon_child_pid;
|
|
|
|
if (pipe(daemon_pipe_fd) < 0) {
|
|
dbg_time("%s Faild to create daemon_pipe_fd: %d (%s)", __func__, errno, strerror(errno));
|
|
return;
|
|
}
|
|
|
|
daemon_child_pid = fork();
|
|
if (daemon_child_pid > 0) {
|
|
struct pollfd pollfds[] = {{daemon_pipe_fd[0], POLLIN, 0}, {0, POLLIN, 0}};
|
|
int ne, ret, nevents = sizeof(pollfds)/sizeof(pollfds[0]);
|
|
int signo;
|
|
|
|
//dbg_time("father");
|
|
|
|
close(daemon_pipe_fd[1]);
|
|
|
|
if (socketpair( AF_LOCAL, SOCK_STREAM, 0, signal_control_fd) < 0 ) {
|
|
dbg_time("%s Faild to create main_control_fd: %d (%s)", __func__, errno, strerror(errno));
|
|
return;
|
|
}
|
|
|
|
pollfds[1].fd = signal_control_fd[1];
|
|
|
|
while (1) {
|
|
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));
|
|
goto __daemon_quit;
|
|
}
|
|
|
|
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", __func__);
|
|
//dbg_time("poll fd = %d, events = 0x%04x", fd, revents);
|
|
if (revents & POLLHUP)
|
|
goto __daemon_quit;
|
|
}
|
|
|
|
if ((revents & POLLIN) && read(fd, &signo, sizeof(signo)) == sizeof(signo)) {
|
|
if (signal_control_fd[1] == fd) {
|
|
if (signo == SIGCHLD) {
|
|
int status;
|
|
int pid = waitpid(daemon_child_pid, &status, 0);
|
|
dbg_time("waitpid pid=%d, status=%x", pid, status);
|
|
goto __daemon_quit;
|
|
} else {
|
|
kill(daemon_child_pid, signo);
|
|
}
|
|
} else if (daemon_pipe_fd[0] == fd) {
|
|
//dbg_time("daemon_pipe_signo = %d", signo);
|
|
goto __daemon_quit;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
__daemon_quit:
|
|
//dbg_time("father exit");
|
|
_exit(0);
|
|
} else if (daemon_child_pid == 0) {
|
|
close(daemon_pipe_fd[0]);
|
|
//dbg_time("child", getpid());
|
|
} else {
|
|
close(daemon_pipe_fd[0]);
|
|
close(daemon_pipe_fd[1]);
|
|
dbg_time("%s Faild to create daemon_child_pid: %d (%s)", __func__, errno, strerror(errno));
|
|
}
|
|
}
|
|
|
|
static void ql_enter_daemon(int signo) {
|
|
if (daemon_pipe_fd[1] > 0)
|
|
if (signo) {
|
|
write(daemon_pipe_fd[1], &signo, sizeof(signo));
|
|
sleep(1);
|
|
}
|
|
close(daemon_pipe_fd[1]);
|
|
daemon_pipe_fd[1] = -1;
|
|
setsid();
|
|
}
|
|
#endif
|
|
|
|
//UINT ifc_get_addr(const char *ifname);
|
|
|
|
static void usbnet_link_change(int link, PROFILE_T *profile) {
|
|
static int s_link = 0;
|
|
|
|
if (s_link == link)
|
|
return;
|
|
|
|
s_link = link;
|
|
|
|
if (link) {
|
|
requestGetIPAddress(profile, IpFamilyV4);
|
|
if (profile->IsDualIPSupported)
|
|
requestGetIPAddress(profile, IpFamilyV6);
|
|
udhcpc_start(profile);
|
|
} else {
|
|
udhcpc_stop(profile);
|
|
}
|
|
|
|
#ifdef LINUX_RIL_SHLIB
|
|
if (link) {
|
|
int timeout = 6;
|
|
while (timeout-- /*&& ifc_get_addr(profile->usbnet_adapter) == 0*/) {
|
|
sleep(1);
|
|
}
|
|
}
|
|
|
|
if (link && requestGetIPAddress(profile, 0x04) == 0) {
|
|
unsigned char *r;
|
|
|
|
dbg_time("Using interface %s", profile->usbnet_adapter);
|
|
r = (unsigned char *)&profile->ipv4.Address;
|
|
dbg_time("local IP address %d.%d.%d.%d", r[3], r[2], r[1], r[0]);
|
|
r = (unsigned char *)&profile->ipv4.Gateway;
|
|
dbg_time("remote IP address %d.%d.%d.%d", r[3], r[2], r[1], r[0]);
|
|
r = (unsigned char *)&profile->ipv4.DnsPrimary;
|
|
dbg_time("primary DNS address %d.%d.%d.%d", r[3], r[2], r[1], r[0]);
|
|
r = (unsigned char *)&profile->ipv4.DnsSecondary;
|
|
dbg_time("secondary DNS address %d.%d.%d.%d", r[3], r[2], r[1], r[0]);
|
|
}
|
|
#endif
|
|
|
|
#ifdef CONFIG_BACKGROUND_WHEN_GET_IP
|
|
if (link && daemon_pipe_fd[1] > 0) {
|
|
int timeout = 6;
|
|
while (timeout-- /*&& ifc_get_addr(profile->usbnet_adapter) == 0*/) {
|
|
sleep(1);
|
|
}
|
|
ql_enter_daemon(SIGUSR1);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
static int check_ipv4_address(PROFILE_T *now_profile) {
|
|
PROFILE_T new_profile_v;
|
|
PROFILE_T *new_profile = &new_profile_v;
|
|
|
|
memcpy(new_profile, now_profile, sizeof(PROFILE_T));
|
|
if (requestGetIPAddress(new_profile, 0x04) == 0) {
|
|
if (new_profile->ipv4.Address != now_profile->ipv4.Address || debug_qmi) {
|
|
unsigned char *l = (unsigned char *)&now_profile->ipv4.Address;
|
|
unsigned char *r = (unsigned char *)&new_profile->ipv4.Address;
|
|
dbg_time("localIP: %d.%d.%d.%d VS remoteIP: %d.%d.%d.%d",
|
|
l[3], l[2], l[1], l[0], r[3], r[2], r[1], r[0]);
|
|
}
|
|
return (new_profile->ipv4.Address == now_profile->ipv4.Address);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static void main_send_event_to_qmidevice(int triger_event) {
|
|
write(qmidevice_control_fd[0], &triger_event, sizeof(triger_event));
|
|
}
|
|
|
|
static void send_signo_to_main(int signo) {
|
|
write(signal_control_fd[0], &signo, sizeof(signo));
|
|
}
|
|
|
|
void qmidevice_send_event_to_main(int triger_event) {
|
|
write(qmidevice_control_fd[1], &triger_event, sizeof(triger_event));
|
|
}
|
|
|
|
#define MAX_PATH 256
|
|
|
|
static int ls_dir(const char *dir, int (*match)(const char *dir, const char *file, void *argv[]), void *argv[])
|
|
{
|
|
DIR *pDir;
|
|
struct dirent* ent = NULL;
|
|
int match_times = 0;
|
|
|
|
pDir = opendir(dir);
|
|
if (pDir == NULL) {
|
|
dbg_time("Cannot open directory: %s, errno: %d (%s)", dir, errno, strerror(errno));
|
|
return 0;
|
|
}
|
|
|
|
while ((ent = readdir(pDir)) != NULL) {
|
|
match_times += match(dir, ent->d_name, argv);
|
|
}
|
|
closedir(pDir);
|
|
|
|
return match_times;
|
|
}
|
|
|
|
static int is_same_linkfile(const char *dir, const char *file, void *argv[])
|
|
{
|
|
const char *qmichannel = (const char *)argv[1];
|
|
char linkname[MAX_PATH];
|
|
char filename[MAX_PATH];
|
|
int linksize;
|
|
|
|
snprintf(linkname, MAX_PATH, "%s/%s", dir, file);
|
|
linksize = readlink(linkname, filename, MAX_PATH);
|
|
if (linksize <= 0)
|
|
return 0;
|
|
|
|
filename[linksize] = 0;
|
|
if (strcmp(filename, qmichannel))
|
|
return 0;
|
|
|
|
dbg_time("%s -> %s", linkname, filename);
|
|
return 1;
|
|
}
|
|
|
|
static int is_brother_process(const char *dir, const char *file, void *argv[])
|
|
{
|
|
//const char *myself = (const char *)argv[0];
|
|
char linkname[MAX_PATH];
|
|
char filename[MAX_PATH];
|
|
int linksize;
|
|
int i = 0, kill_timeout = 15;
|
|
pid_t pid;
|
|
|
|
//dbg_time("%s", file);
|
|
while (file[i]) {
|
|
if (!isdigit(file[i]))
|
|
break;
|
|
i++;
|
|
}
|
|
|
|
if (file[i]) {
|
|
//dbg_time("%s not digit", file);
|
|
return 0;
|
|
}
|
|
|
|
snprintf(linkname, MAX_PATH, "%s/%s/exe", dir, file);
|
|
linksize = readlink(linkname, filename, MAX_PATH);
|
|
if (linksize <= 0)
|
|
return 0;
|
|
|
|
filename[linksize] = 0;
|
|
#if 0 //check all process
|
|
if (strcmp(filename, myself))
|
|
return 0;
|
|
#endif
|
|
|
|
pid = atoi(file);
|
|
if (pid >= getpid())
|
|
return 0;
|
|
|
|
snprintf(linkname, MAX_PATH, "%s/%s/fd", dir, file);
|
|
if (!ls_dir(linkname, is_same_linkfile, argv))
|
|
return 0;
|
|
|
|
dbg_time("%s/%s/exe -> %s", dir, file, filename);
|
|
while (kill_timeout-- && !kill(pid, 0))
|
|
{
|
|
kill(pid, SIGTERM);
|
|
sleep(1);
|
|
}
|
|
if (!kill(pid, 0))
|
|
{
|
|
dbg_time("force kill %s/%s/exe -> %s", dir, file, filename);
|
|
kill(pid, SIGKILL);
|
|
sleep(1);
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int kill_brothers(const char *qmichannel)
|
|
{
|
|
char myself[MAX_PATH];
|
|
int filenamesize;
|
|
void *argv[2] = {myself, (void *)qmichannel};
|
|
|
|
filenamesize = readlink("/proc/self/exe", myself, MAX_PATH);
|
|
if (filenamesize <= 0)
|
|
return 0;
|
|
myself[filenamesize] = 0;
|
|
|
|
if (ls_dir("/proc", is_brother_process, argv))
|
|
sleep(1);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void ql_sigaction(int signo) {
|
|
if (SIGCHLD == signo)
|
|
waitpid(-1, NULL, WNOHANG);
|
|
else if (SIGALRM == signo)
|
|
send_signo_to_main(SIGUSR1);
|
|
else
|
|
{
|
|
if (SIGTERM == signo || SIGHUP == signo || SIGINT == signo)
|
|
main_loop = 0;
|
|
send_signo_to_main(signo);
|
|
main_send_event_to_qmidevice(signo); //main may be wating qmi response
|
|
}
|
|
}
|
|
|
|
pthread_t gQmiThreadID;
|
|
|
|
static int usage(const char *progname) {
|
|
dbg_time("Usage: %s [-s [apn [user password auth]]] [-p pincode] [-f logfilename] ", progname);
|
|
dbg_time("-s [apn [user password auth]] Set apn/user/password/auth get from your network provider");
|
|
dbg_time("-p pincode Verify sim card pin if sim card is locked");
|
|
dbg_time("-f logfilename Save log message of this program to file");
|
|
dbg_time("Example 1: %s ", progname);
|
|
dbg_time("Example 2: %s -s 3gnet ", progname);
|
|
dbg_time("Example 3: %s -s 3gnet carl 1234 0 -p 1234 -f gobinet_log.txt", progname);
|
|
return 0;
|
|
}
|
|
static int charsplit(const char *src,char* desc,int n,const char* splitStr)
|
|
{
|
|
char* p;
|
|
char*p1;
|
|
int len;
|
|
|
|
len=strlen(splitStr);
|
|
p=strstr(src,splitStr);
|
|
if(p==NULL)
|
|
return -1;
|
|
p1=strstr(p,"\n");
|
|
if(p1==NULL)
|
|
return -1;
|
|
memset(desc,0,n);
|
|
memcpy(desc,p+len,p1-p-len);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int get_dev_major_minor(char* path, int *major, int *minor)
|
|
{
|
|
int fd = -1;
|
|
char desc[128] = {0};
|
|
char devmajor[64],devminor[64];
|
|
int n = 0;
|
|
if(access(path, R_OK | W_OK))
|
|
{
|
|
return 1;
|
|
}
|
|
if((fd = open(path, O_RDWR)) < 0)
|
|
{
|
|
return 1;
|
|
}
|
|
n = read(fd , desc, sizeof(desc));
|
|
if(n == sizeof(desc))
|
|
{
|
|
dbg_time("may be overflow");
|
|
}
|
|
close(fd);
|
|
if(charsplit(desc,devmajor,64,"MAJOR=")==-1 ||
|
|
charsplit(desc,devminor,64,"MINOR=")==-1 )
|
|
{
|
|
return 2;
|
|
}
|
|
*major = atoi(devmajor);
|
|
*minor = atoi(devminor);
|
|
return 0;
|
|
}
|
|
|
|
static int qmidevice_detect(char **pp_qmichannel, char **pp_usbnet_adapter) {
|
|
struct dirent* ent = NULL;
|
|
DIR *pDir;
|
|
|
|
char dir[255] = "/sys/bus/usb/devices";
|
|
int major = 0, minor = 0;
|
|
pDir = opendir(dir);
|
|
if (pDir) {
|
|
while ((ent = readdir(pDir)) != NULL) {
|
|
struct dirent* subent = NULL;
|
|
DIR *psubDir;
|
|
char subdir[255];
|
|
char subdir2[255 * 2];
|
|
|
|
char idVendor[4+1] = {0};
|
|
char idProduct[4+1] = {0};
|
|
int fd = 0;
|
|
|
|
char netcard[32] = "\0";
|
|
char qmifile[32] = "\0";
|
|
|
|
snprintf(subdir, sizeof(subdir), "%s/%s/idVendor", dir, ent->d_name);
|
|
fd = open(subdir, O_RDONLY);
|
|
if (fd > 0) {
|
|
read(fd, idVendor, 4);
|
|
close(fd);
|
|
}
|
|
|
|
snprintf(subdir, sizeof(subdir), "%s/%s/idProduct", dir, ent->d_name);
|
|
fd = open(subdir, O_RDONLY);
|
|
if (fd > 0) {
|
|
read(fd, idProduct, 4);
|
|
close(fd);
|
|
}
|
|
|
|
if (!strncasecmp(idVendor, "05c6", 4) || !strncasecmp(idVendor, "2c7c", 4))
|
|
;
|
|
else
|
|
continue;
|
|
|
|
dbg_time("Find %s/%s idVendor=%s idProduct=%s", dir, ent->d_name, idVendor, idProduct);
|
|
|
|
snprintf(subdir, sizeof(subdir), "%s/%s:1.4/net", dir, ent->d_name);
|
|
psubDir = opendir(subdir);
|
|
if (psubDir == NULL) {
|
|
dbg_time("Cannot open directory: %s, errno: %d (%s)", subdir, errno, strerror(errno));
|
|
continue;
|
|
}
|
|
|
|
while ((subent = readdir(psubDir)) != NULL) {
|
|
if (subent->d_name[0] == '.')
|
|
continue;
|
|
dbg_time("Find %s/%s", subdir, subent->d_name);
|
|
dbg_time("Find usbnet_adapter = %s", subent->d_name);
|
|
strcpy(netcard, subent->d_name);
|
|
break;
|
|
}
|
|
|
|
closedir(psubDir);
|
|
|
|
if (netcard[0]) {
|
|
} else {
|
|
continue;
|
|
}
|
|
|
|
if (*pp_usbnet_adapter && strcmp(*pp_usbnet_adapter, netcard))
|
|
continue;
|
|
|
|
snprintf(subdir, sizeof(subdir), "%s/%s:1.4/GobiQMI", dir, ent->d_name);
|
|
if (access(subdir, R_OK)) {
|
|
snprintf(subdir, sizeof(subdir), "%s/%s:1.4/usbmisc", dir, ent->d_name);
|
|
if (access(subdir, R_OK)) {
|
|
snprintf(subdir, sizeof(subdir), "%s/%s:1.4/usb", dir, ent->d_name);
|
|
if (access(subdir, R_OK)) {
|
|
dbg_time("no GobiQMI/usbmic/usb found in %s/%s:1.4", dir, ent->d_name);
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
|
|
psubDir = opendir(subdir);
|
|
if (pDir == NULL) {
|
|
dbg_time("Cannot open directory: %s, errno: %d (%s)", dir, errno, strerror(errno));
|
|
continue;
|
|
}
|
|
|
|
while ((subent = readdir(psubDir)) != NULL) {
|
|
if (subent->d_name[0] == '.')
|
|
continue;
|
|
dbg_time("Find %s/%s", subdir, subent->d_name);
|
|
dbg_time("Find qmichannel = /dev/%s", subent->d_name);
|
|
snprintf(qmifile, sizeof(qmifile), "/dev/%s", subent->d_name);
|
|
|
|
//get major minor
|
|
snprintf(subdir2, sizeof(subdir), "%s/%s/uevent",subdir, subent->d_name);
|
|
if(!get_dev_major_minor(subdir2, &major, &minor))
|
|
{
|
|
//dbg_time("%s major = %d, minor = %d\n",qmifile, major, minor);
|
|
}else
|
|
{
|
|
dbg_time("get %s major and minor failed\n",qmifile);
|
|
}
|
|
//get major minor
|
|
|
|
if((fd = open(qmifile, R_OK)) < 0)
|
|
{
|
|
dbg_time("%s open failed", qmifile);
|
|
dbg_time("please mknod %s c %d %d", qmifile, major, minor);
|
|
}else
|
|
{
|
|
close(fd);
|
|
}
|
|
break;
|
|
}
|
|
|
|
closedir(psubDir);
|
|
|
|
if (netcard[0] && qmifile[0]) {
|
|
*pp_qmichannel = strdup(qmifile);
|
|
*pp_usbnet_adapter = strdup(netcard);
|
|
closedir(pDir);
|
|
return 1;
|
|
}
|
|
|
|
}
|
|
|
|
closedir(pDir);
|
|
}
|
|
|
|
if ((pDir = opendir("/dev")) == NULL) {
|
|
dbg_time("Cannot open directory: %s, errno:%d (%s)", "/dev", errno, strerror(errno));
|
|
return -ENODEV;
|
|
}
|
|
|
|
while ((ent = readdir(pDir)) != NULL) {
|
|
if ((strncmp(ent->d_name, "cdc-wdm", strlen("cdc-wdm")) == 0) || (strncmp(ent->d_name, "qcqmi", strlen("qcqmi")) == 0)) {
|
|
char net_path[64];
|
|
|
|
*pp_qmichannel = (char *)malloc(32);
|
|
sprintf(*pp_qmichannel, "/dev/%s", ent->d_name);
|
|
dbg_time("Find qmichannel = %s", *pp_qmichannel);
|
|
|
|
if (strncmp(ent->d_name, "cdc-wdm", strlen("cdc-wdm")) == 0)
|
|
sprintf(net_path, "/sys/class/net/wwan%s", &ent->d_name[strlen("cdc-wdm")]);
|
|
else
|
|
{
|
|
sprintf(net_path, "/sys/class/net/usb%s", &ent->d_name[strlen("qcqmi")]);
|
|
#if 0//ndef ANDROID
|
|
if (kernel_version >= KVERSION( 2,6,39 ))
|
|
sprintf(net_path, "/sys/class/net/eth%s", &ent->d_name[strlen("qcqmi")]);
|
|
#else
|
|
if (access(net_path, R_OK) && errno == ENOENT)
|
|
sprintf(net_path, "/sys/class/net/eth%s", &ent->d_name[strlen("qcqmi")]);
|
|
#endif
|
|
#if 0 //openWRT like use ppp# or lte#
|
|
if (access(net_path, R_OK) && errno == ENOENT)
|
|
sprintf(net_path, "/sys/class/net/ppp%s", &ent->d_name[strlen("qcqmi")]);
|
|
if (access(net_path, R_OK) && errno == ENOENT)
|
|
sprintf(net_path, "/sys/class/net/lte%s", &ent->d_name[strlen("qcqmi")]);
|
|
#endif
|
|
}
|
|
|
|
if (access(net_path, R_OK) == 0)
|
|
{
|
|
if (*pp_usbnet_adapter && strcmp(*pp_usbnet_adapter, (net_path + strlen("/sys/class/net/"))))
|
|
{
|
|
free(*pp_qmichannel); *pp_qmichannel = NULL;
|
|
continue;
|
|
}
|
|
*pp_usbnet_adapter = strdup(net_path + strlen("/sys/class/net/"));
|
|
dbg_time("Find usbnet_adapter = %s", *pp_usbnet_adapter);
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
dbg_time("Failed to access %s, errno:%d (%s)", net_path, errno, strerror(errno));
|
|
free(*pp_qmichannel); *pp_qmichannel = NULL;
|
|
}
|
|
}
|
|
}
|
|
closedir(pDir);
|
|
|
|
return (*pp_qmichannel && *pp_usbnet_adapter);
|
|
}
|
|
|
|
#if defined(ANDROID) || defined(LINUX_RIL_SHLIB)
|
|
int quectel_CM(int argc, char *argv[])
|
|
#else
|
|
int main(int argc, char *argv[])
|
|
#endif
|
|
{
|
|
int triger_event = 0;
|
|
int opt = 1;
|
|
int signo;
|
|
#ifdef CONFIG_SIM
|
|
SIM_Status SIMStatus;
|
|
#endif
|
|
UCHAR PSAttachedState;
|
|
UCHAR IPv4ConnectionStatus = 0xff; //unknow state
|
|
UCHAR IPV6ConnectionStatus = 0xff; //unknow state
|
|
int qmierr = 0;
|
|
char * save_usbnet_adapter = NULL;
|
|
PROFILE_T profile;
|
|
#ifdef CONFIG_RESET_RADIO
|
|
struct timeval resetRadioTime = {0};
|
|
struct timeval nowTime;
|
|
gettimeofday(&resetRadioTime, (struct timezone *) NULL);
|
|
#endif
|
|
|
|
memset(&profile, 0x00, sizeof(profile));
|
|
profile.pdp = CONFIG_DEFAULT_PDP;
|
|
|
|
if (!strcmp(argv[argc-1], "&"))
|
|
argc--;
|
|
|
|
opt = 1;
|
|
while (opt < argc)
|
|
{
|
|
if (argv[opt][0] != '-')
|
|
return usage(argv[0]);
|
|
|
|
switch (argv[opt++][1])
|
|
{
|
|
#define has_more_argv() ((opt < argc) && (argv[opt][0] != '-'))
|
|
case 's':
|
|
profile.apn = profile.user = profile.password = "";
|
|
if (has_more_argv())
|
|
profile.apn = argv[opt++];
|
|
if (has_more_argv())
|
|
profile.user = argv[opt++];
|
|
if (has_more_argv())
|
|
{
|
|
profile.password = argv[opt++];
|
|
if (profile.password && profile.password[0])
|
|
profile.auth = 2; //default chap, customers may miss auth
|
|
}
|
|
if (has_more_argv())
|
|
profile.auth = argv[opt++][0] - '0';
|
|
break;
|
|
|
|
case 'p':
|
|
if (has_more_argv())
|
|
profile.pincode = argv[opt++];
|
|
break;
|
|
|
|
case 'n':
|
|
if (has_more_argv())
|
|
profile.pdp = argv[opt++][0] - '0';
|
|
break;
|
|
|
|
case 'f':
|
|
if (has_more_argv())
|
|
{
|
|
const char * filename = argv[opt++];
|
|
logfilefp = fopen(filename, "a+");
|
|
if (!logfilefp) {
|
|
dbg_time("Fail to open %s, errno: %d(%s)", filename, errno, strerror(errno));
|
|
}
|
|
}
|
|
break;
|
|
|
|
case 'i':
|
|
if (has_more_argv())
|
|
profile.usbnet_adapter = save_usbnet_adapter = argv[opt++];
|
|
break;
|
|
|
|
case 'v':
|
|
debug_qmi = 1;
|
|
break;
|
|
|
|
case 'l':
|
|
main_loop = 1;
|
|
break;
|
|
|
|
case '6':
|
|
profile.IsDualIPSupported |= (1 << IpFamilyV6); //support ipv4&ipv6
|
|
break;
|
|
|
|
case 'd':
|
|
if (has_more_argv()) {
|
|
profile.qmichannel = argv[opt++];
|
|
if (!strncmp(profile.qmichannel, "/dev/mhi_QMI", strlen("/dev/mhi_QMI")))
|
|
profile.usbnet_adapter = "rmnet_mhi0";
|
|
}
|
|
break;
|
|
|
|
default:
|
|
return usage(argv[0]);
|
|
break;
|
|
}
|
|
}
|
|
|
|
dbg_time("WCDMA<E_QConnectManager_Linux&Android_V1.1.45");
|
|
dbg_time("%s profile[%d] = %s/%s/%s/%d, pincode = %s", argv[0], profile.pdp, profile.apn, profile.user, profile.password, profile.auth, profile.pincode);
|
|
|
|
signal(SIGUSR1, ql_sigaction);
|
|
signal(SIGUSR2, ql_sigaction);
|
|
signal(SIGINT, ql_sigaction);
|
|
signal(SIGTERM, ql_sigaction);
|
|
signal(SIGHUP, ql_sigaction);
|
|
signal(SIGCHLD, ql_sigaction);
|
|
signal(SIGALRM, ql_sigaction);
|
|
|
|
#ifdef CONFIG_BACKGROUND_WHEN_GET_IP
|
|
ql_prepare_daemon();
|
|
#endif
|
|
|
|
if (socketpair( AF_LOCAL, SOCK_STREAM, 0, signal_control_fd) < 0 ) {
|
|
dbg_time("%s Faild to create main_control_fd: %d (%s)", __func__, errno, strerror(errno));
|
|
return -1;
|
|
}
|
|
|
|
if ( socketpair( AF_LOCAL, SOCK_STREAM, 0, qmidevice_control_fd ) < 0 ) {
|
|
dbg_time("%s Failed to create thread control socket pair: %d (%s)", __func__, errno, strerror(errno));
|
|
return 0;
|
|
}
|
|
|
|
//sudo apt-get install udhcpc
|
|
//sudo apt-get remove ModemManager
|
|
__main_loop:
|
|
while (!profile.qmichannel)
|
|
{
|
|
if (qmidevice_detect(&profile.qmichannel, &profile.usbnet_adapter))
|
|
break;
|
|
if (main_loop)
|
|
{
|
|
int wait_for_device = 3000;
|
|
dbg_time("Wait for Quectel modules connect");
|
|
while (wait_for_device && main_loop) {
|
|
wait_for_device -= 100;
|
|
usleep(100*1000);
|
|
}
|
|
continue;
|
|
}
|
|
dbg_time("Cannot find qmichannel(%s) usbnet_adapter(%s) for Quectel modules", profile.qmichannel, profile.usbnet_adapter);
|
|
return -ENODEV;
|
|
}
|
|
|
|
if (!strncmp(profile.qmichannel, "/dev/qcqmi", strlen("/dev/qcqmi"))) {
|
|
char MODE_FILE[128];
|
|
int mode_fd;
|
|
|
|
snprintf(MODE_FILE, sizeof(MODE_FILE), "/sys/class/net/%s/qmap_mode", profile.usbnet_adapter);
|
|
mode_fd = open(MODE_FILE, O_RDONLY);
|
|
|
|
if (mode_fd > 0) {
|
|
char qmap_mode[2] = {0, 0};
|
|
read(mode_fd, &qmap_mode, sizeof(qmap_mode));
|
|
close(mode_fd);
|
|
profile.qmap_mode = qmap_mode[0] - '0';
|
|
}
|
|
|
|
if (profile.qmap_mode) {
|
|
char qmapnet_adapter[128];
|
|
|
|
sprintf(qmapnet_adapter, "/sys/class/net/%s.%d", profile.usbnet_adapter, profile.pdp);
|
|
if (!access(qmapnet_adapter, R_OK)) {
|
|
static char netcard[32] = "\0";
|
|
|
|
sprintf(netcard, "%s.%d", profile.usbnet_adapter, profile.pdp);
|
|
profile.qmapnet_adapter = netcard;
|
|
} else {
|
|
profile.qmapnet_adapter = profile.usbnet_adapter;
|
|
}
|
|
dbg_time("Find qmapnet_adapter = %s", profile.qmapnet_adapter);
|
|
}
|
|
} else if(!strncmp(profile.qmichannel, "/dev/cdc-wdm", strlen("/dev/cdc-wdm"))) {
|
|
if (!access("/sys/module/qmi_wwan/parameters/rx_qmap", R_OK)) {
|
|
char qmapnet_adapter[128];
|
|
|
|
sprintf(qmapnet_adapter, "/sys/class/net/%s.%d", profile.usbnet_adapter, profile.pdp);
|
|
if (!access(qmapnet_adapter, R_OK)) {
|
|
static char netcard[32] = "\0";
|
|
|
|
sprintf(netcard, "%s.%d", profile.usbnet_adapter, profile.pdp);
|
|
profile.qmapnet_adapter = netcard;
|
|
} else {
|
|
profile.qmapnet_adapter = profile.usbnet_adapter;
|
|
}
|
|
dbg_time("Find qmapnet_adapter = %s", profile.qmapnet_adapter);
|
|
}
|
|
} else if (!strncmp(profile.qmichannel, "/dev/mhi_QMI", strlen("/dev/mhi_QMI"))) {
|
|
profile.qmapnet_adapter = profile.usbnet_adapter;
|
|
}
|
|
|
|
if (access(profile.qmichannel, R_OK | W_OK)) {
|
|
dbg_time("Fail to access %s, errno: %d (%s)", profile.qmichannel, errno, strerror(errno));
|
|
return errno;
|
|
}
|
|
|
|
#if 0 //for test only, make fd > 255
|
|
{
|
|
int max_dup = 255;
|
|
while (max_dup--)
|
|
dup(0);
|
|
}
|
|
#endif
|
|
|
|
if (profile.qmapnet_adapter == NULL || profile.qmapnet_adapter == profile.usbnet_adapter)
|
|
kill_brothers(profile.qmichannel);
|
|
|
|
qmichannel = profile.qmichannel;
|
|
if (!strncmp(profile.qmichannel, "/dev/qcqmi", strlen("/dev/qcqmi")))
|
|
{
|
|
if (pthread_create( &gQmiThreadID, 0, GobiNetThread, (void *)&profile) != 0)
|
|
{
|
|
dbg_time("%s Failed to create GobiNetThread: %d (%s)", __func__, errno, strerror(errno));
|
|
return 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (pthread_create( &gQmiThreadID, 0, QmiWwanThread, (void *)&profile) != 0)
|
|
{
|
|
dbg_time("%s Failed to create QmiWwanThread: %d (%s)", __func__, errno, strerror(errno));
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
if ((read(qmidevice_control_fd[0], &triger_event, sizeof(triger_event)) != sizeof(triger_event))
|
|
|| (triger_event != RIL_INDICATE_DEVICE_CONNECTED)) {
|
|
dbg_time("%s Failed to init QMIThread: %d (%s)", __func__, errno, strerror(errno));
|
|
return 0;
|
|
}
|
|
|
|
if (!strncmp(profile.qmichannel, "/dev/cdc-wdm", strlen("/dev/cdc-wdm"))
|
|
|| !strncmp(profile.qmichannel, "/dev/mhi_QMI", strlen("/dev/mhi_QMI"))) {
|
|
if (QmiWwanInit(&profile)) {
|
|
dbg_time("%s Failed to QmiWwanInit: %d (%s)", __func__, errno, strerror(errno));
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
#ifdef CONFIG_VERSION
|
|
requestBaseBandVersion(NULL);
|
|
#endif
|
|
requestSetEthMode(&profile);
|
|
#if 0
|
|
if (profile.rawIP && !strncmp(profile.qmichannel, "/dev/cdc-wdm", strlen("/dev/cdc-wdm"))) {
|
|
char raw_ip_switch[128] = {0};
|
|
sprintf(raw_ip_switch, "/sys/class/net/%s/qmi/raw_ip", profile.usbnet_adapter);
|
|
if (!access(raw_ip_switch, R_OK)) {
|
|
int raw_ip_fd = -1;
|
|
raw_ip_fd = open(raw_ip_switch, O_RDWR);
|
|
if (raw_ip_fd >= 0) {
|
|
write(raw_ip_fd, "1", strlen("1"));
|
|
close(raw_ip_fd);
|
|
raw_ip_fd = -1;
|
|
} else {
|
|
dbg_time("open %s failed, errno = %d(%s)\n", raw_ip_switch, errno, strerror(errno));
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
#ifdef CONFIG_SIM
|
|
qmierr = requestGetSIMStatus(&SIMStatus);
|
|
while (qmierr == QMI_ERR_OP_DEVICE_UNSUPPORTED) {
|
|
sleep(1);
|
|
qmierr = requestGetSIMStatus(&SIMStatus);
|
|
}
|
|
if ((SIMStatus == SIM_PIN) && profile.pincode) {
|
|
requestEnterSimPin(profile.pincode);
|
|
}
|
|
#ifdef CONFIG_IMSI_ICCID
|
|
if (SIMStatus == SIM_READY) {
|
|
requestGetICCID();
|
|
requestGetIMSI();
|
|
}
|
|
#endif
|
|
#endif
|
|
#ifdef CONFIG_APN
|
|
if (profile.apn || profile.user || profile.password) {
|
|
requestSetProfile(&profile);
|
|
}
|
|
requestGetProfile(&profile);
|
|
#endif
|
|
requestRegistrationState(&PSAttachedState);
|
|
|
|
if (!requestQueryDataCall(&IPv4ConnectionStatus, IpFamilyV4) && (QWDS_PKT_DATA_CONNECTED == IPv4ConnectionStatus))
|
|
usbnet_link_change(1, &profile);
|
|
else
|
|
usbnet_link_change(0, &profile);
|
|
|
|
send_signo_to_main(SIGUSR1);
|
|
|
|
#ifdef CONFIG_PID_FILE_FORMAT
|
|
{
|
|
char cmd[255];
|
|
sprintf(cmd, "echo %d > " CONFIG_PID_FILE_FORMAT, getpid(), profile.usbnet_adapter);
|
|
system(cmd);
|
|
}
|
|
#endif
|
|
|
|
while (1)
|
|
{
|
|
struct pollfd pollfds[] = {{signal_control_fd[1], POLLIN, 0}, {qmidevice_control_fd[0], POLLIN, 0}};
|
|
int ne, ret, nevents = sizeof(pollfds)/sizeof(pollfds[0]);
|
|
|
|
do {
|
|
ret = poll(pollfds, nevents, 15*1000);
|
|
} while ((ret < 0) && (errno == EINTR));
|
|
|
|
if (ret == 0)
|
|
{
|
|
send_signo_to_main(SIGUSR2);
|
|
continue;
|
|
}
|
|
|
|
if (ret <= 0) {
|
|
dbg_time("%s poll=%d, errno: %d (%s)", __func__, ret, errno, strerror(errno));
|
|
goto __main_quit;
|
|
}
|
|
|
|
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", __func__);
|
|
dbg_time("epoll fd = %d, events = 0x%04x", fd, revents);
|
|
main_send_event_to_qmidevice(RIL_REQUEST_QUIT);
|
|
if (revents & POLLHUP)
|
|
goto __main_quit;
|
|
}
|
|
|
|
if ((revents & POLLIN) == 0)
|
|
continue;
|
|
|
|
if (fd == signal_control_fd[1])
|
|
{
|
|
if (read(fd, &signo, sizeof(signo)) == sizeof(signo))
|
|
{
|
|
alarm(0);
|
|
switch (signo)
|
|
{
|
|
case SIGUSR1:
|
|
requestQueryDataCall(&IPv4ConnectionStatus, IpFamilyV4);
|
|
if (QWDS_PKT_DATA_CONNECTED != IPv4ConnectionStatus)
|
|
{
|
|
usbnet_link_change(0, &profile);
|
|
requestRegistrationState(&PSAttachedState);
|
|
|
|
if (PSAttachedState == 1) {
|
|
qmierr = requestSetupDataCall(&profile, IpFamilyV4);
|
|
|
|
if ((qmierr > 0) && profile.user && profile.user[0] && profile.password && profile.password[0]) {
|
|
int old_auto = profile.auth;
|
|
|
|
//may be fail because wrong auth mode, try pap->chap, or chap->pap
|
|
profile.auth = (profile.auth == 1) ? 2 : 1;
|
|
qmierr = requestSetupDataCall(&profile, IpFamilyV4);
|
|
|
|
if (qmierr)
|
|
profile.auth = old_auto; //still fail, restore old auth moe
|
|
}
|
|
|
|
//succssful setup data call
|
|
if (!qmierr && profile.IsDualIPSupported) {
|
|
requestSetupDataCall(&profile, IpFamilyV6);
|
|
}
|
|
|
|
if (!qmierr)
|
|
continue;
|
|
}
|
|
|
|
#ifdef CONFIG_EXIT_WHEN_DIAL_FAILED
|
|
kill(getpid(), SIGTERM);
|
|
#endif
|
|
#ifdef CONFIG_RESET_RADIO
|
|
gettimeofday(&nowTime, (struct timezone *) NULL);
|
|
if (abs(nowTime.tv_sec - resetRadioTime.tv_sec) > CONFIG_RESET_RADIO) {
|
|
resetRadioTime = nowTime;
|
|
//requestSetOperatingMode(0x06); //same as AT+CFUN=0
|
|
requestSetOperatingMode(0x01); //same as AT+CFUN=4
|
|
requestSetOperatingMode(0x00); //same as AT+CFUN=1
|
|
}
|
|
#endif
|
|
alarm(5); //try to setup data call 5 seconds later
|
|
}
|
|
break;
|
|
|
|
case SIGUSR2:
|
|
if (QWDS_PKT_DATA_CONNECTED == IPv4ConnectionStatus)
|
|
requestQueryDataCall(&IPv4ConnectionStatus, IpFamilyV4);
|
|
|
|
//local ip is different with remote ip
|
|
if (QWDS_PKT_DATA_CONNECTED == IPv4ConnectionStatus && check_ipv4_address(&profile) == 0) {
|
|
requestDeactivateDefaultPDP(&profile, IpFamilyV4);
|
|
IPv4ConnectionStatus = QWDS_PKT_DATA_DISCONNECTED;
|
|
}
|
|
|
|
if (QWDS_PKT_DATA_CONNECTED != IPv4ConnectionStatus) {
|
|
#if defined(ANDROID) || defined(LINUX_RIL_SHLIB)
|
|
kill(getpid(), SIGTERM); //android will setup data call again
|
|
#else
|
|
send_signo_to_main(SIGUSR1);
|
|
#endif
|
|
}
|
|
break;
|
|
|
|
case SIGTERM:
|
|
case SIGHUP:
|
|
case SIGINT:
|
|
if (QWDS_PKT_DATA_CONNECTED == IPv4ConnectionStatus) {
|
|
requestDeactivateDefaultPDP(&profile, IpFamilyV4);
|
|
if (profile.IsDualIPSupported)
|
|
requestDeactivateDefaultPDP(&profile, IpFamilyV6);
|
|
}
|
|
usbnet_link_change(0, &profile);
|
|
if (!strncmp(profile.qmichannel, "/dev/cdc-wdm", strlen("/dev/cdc-wdm"))
|
|
|| !strncmp(profile.qmichannel, "/dev/mhi_QMI", strlen("/dev/mhi_QMI")))
|
|
QmiWwanDeInit();
|
|
main_send_event_to_qmidevice(RIL_REQUEST_QUIT);
|
|
goto __main_quit;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (fd == qmidevice_control_fd[0]) {
|
|
if (read(fd, &triger_event, sizeof(triger_event)) == sizeof(triger_event)) {
|
|
switch (triger_event) {
|
|
case RIL_INDICATE_DEVICE_DISCONNECTED:
|
|
usbnet_link_change(0, &profile);
|
|
if (main_loop)
|
|
{
|
|
if (pthread_join(gQmiThreadID, NULL)) {
|
|
dbg_time("%s Error joining to listener thread (%s)", __func__, strerror(errno));
|
|
}
|
|
profile.qmichannel = NULL;
|
|
profile.usbnet_adapter = save_usbnet_adapter;
|
|
goto __main_loop;
|
|
}
|
|
goto __main_quit;
|
|
break;
|
|
|
|
case RIL_UNSOL_RESPONSE_VOICE_NETWORK_STATE_CHANGED:
|
|
requestRegistrationState(&PSAttachedState);
|
|
if (PSAttachedState == 1 && QWDS_PKT_DATA_DISCONNECTED == IPv4ConnectionStatus)
|
|
send_signo_to_main(SIGUSR1);
|
|
break;
|
|
|
|
case RIL_UNSOL_DATA_CALL_LIST_CHANGED:
|
|
{
|
|
UCHAR oldConnectionStatus = IPv4ConnectionStatus;
|
|
requestQueryDataCall(&IPv4ConnectionStatus, IpFamilyV4);
|
|
if (profile.IsDualIPSupported)
|
|
requestQueryDataCall(&IPV6ConnectionStatus, IpFamilyV6);
|
|
if (QWDS_PKT_DATA_CONNECTED != IPv4ConnectionStatus)
|
|
{
|
|
usbnet_link_change(0, &profile);
|
|
if (oldConnectionStatus == QWDS_PKT_DATA_CONNECTED){ //connected change to disconnect
|
|
#if defined(ANDROID) || defined(LINUX_RIL_SHLIB)
|
|
kill(getpid(), SIGTERM); //android will setup data call again
|
|
#else
|
|
send_signo_to_main(SIGUSR1);
|
|
#endif
|
|
}
|
|
} else if (QWDS_PKT_DATA_CONNECTED == IPv4ConnectionStatus) {
|
|
usbnet_link_change(1, &profile);
|
|
if (oldConnectionStatus == QWDS_PKT_DATA_CONNECTED) { //receive two CONNECT IND?
|
|
send_signo_to_main(SIGUSR2);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
__main_quit:
|
|
usbnet_link_change(0, &profile);
|
|
if (pthread_join(gQmiThreadID, NULL)) {
|
|
dbg_time("%s Error joining to listener thread (%s)", __func__, strerror(errno));
|
|
}
|
|
close(signal_control_fd[0]);
|
|
close(signal_control_fd[1]);
|
|
close(qmidevice_control_fd[0]);
|
|
close(qmidevice_control_fd[1]);
|
|
dbg_time("%s exit", __func__);
|
|
if (logfilefp)
|
|
fclose(logfilefp);
|
|
|
|
#ifdef CONFIG_PID_FILE_FORMAT
|
|
{
|
|
char cmd[255];
|
|
sprintf(cmd, "rm " CONFIG_PID_FILE_FORMAT, profile.usbnet_adapter);
|
|
system(cmd);
|
|
}
|
|
#endif
|
|
|
|
return 0;
|
|
}
|