/*
* ~ Simple 3GPP Network Scanner using the Qualcomm MSM Interface (QMI) ~
*
* by plastinka
*
* WARNING: This is Research Code! For testing purposes only!
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License 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.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*
*
* Build command: gcc -o qmi_scanner qmi_scanner.c
*/
#include <sys/types.h>
#include <sys/select.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <asm/types.h>
#include <termio.h>
#include <syslog.h>
#include <time.h>
#include <getopt.h>
#include <termios.h>
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <unistd.h>
#ifndef TRUE
#define TRUE 1
#endif
#ifndef FALSE
#define FALSE 0
#endif
#define QMI_MSG_REQUEST 0x00
#define QMI_MSG_RESPONSE 0x02
#define QMI_MSG_INDICATION 0x04
#define QMI_CTL 0x00 //Control Service
#define QMI_WDS 0x01 //Wireless Data Service
#define QMI_DMS 0x02 //Device Managment Service
#define QMI_NAS 0x03 //Network Access Service
#define QMI_CTL_SET_INSTANCE_ID 0x0020 //Set the unique link instance ID
#define QMI_CTL_GET_VERSION_INFO 0x0021 //Get supported service version info
#define QMI_CTL_GET_CLIENT_ID 0x0022 //Get a unique client ID
#define QMI_CTL_RELEASE_CLIENT_ID 0x0023 //Release the unique client ID
#define QMI_CTL_REVOKE_CLIENT_ID_IND 0x0024 //Indication of client ID revocation
#define QMI_CTL_INVALID_CLIENT_ID 0x0025 //Indication of invalid client ID
#define QMI_CTL_SET_DATA_FORMAT 0x0026 //Set host driver data format
#define QMI_CTL_SYNC 0x0027 //Synchronize client/server
#define QMI_CTL_SYNC_IND 0x0027 //Synchronize indication
#define QMI_CTL_SET_EVENT 0x0028 //Set event report conditions
#define QMI_CTL_EVENT_IND 0x0028 //Event report indication
#define QMI_DMS_GET_BAND_CAPS 0x0045 //This message requests the band capability of the device
#define QMI_NAS_RESET 0x0000 //Reset NAS service state variables
#define QMI_NAS_ABORT 0x0001 //Abort previously issued NAS command
#define QMI_NAS_SET_EVENT 0x0002 //Set NAS state report conditions
#define QMI_NAS_EVENT_IND 0x0002 //Connection state report indication
#define QMI_NAS_SET_REG_EVENT 0x0003 //Set NAS registration report conditions
#define QMI_NAS_SCAN_NETS 0x0021 //Scan for visible network
#define QMI_NAS_GET_RF_BAND_INFO 0x0031 //Queries radio band/channel information regarding the system currently providing service
#define QMI_NAS_SET_SYS_SELECTION 0x0033 //Sets the different system selection preferences of the device
#define QMI_NAS_GET_SYS_SELECTION 0x0034 //Queries the different system selection preferences of the device
#define QMI_RESULT_SUCCESS 0x0000
#define QMI_RESULT_FAILURE 0x0001
#define QMI_ERR_NONE 0x0000
#define QMI_ERR_MALFORMED_MSG 0x0001
#define QMI_ERR_NO_MEMORY 0x0002
#define QMI_ERR_INTERNAL 0x0003
#define QMI_ERR_ABORTED 0x0004
#define QMI_ERR_CLIENT_IDS_EXHAUSTED 0x0005
#define QMI_ERR_INVALID_CLIENT_ID 0x0007
#define QMI_TLV_TYPE_RESULT_CODE 0x02
//CTL is always client zero
#define QMI_CTL_CLIENT 0x0000
#define RAT_GSM 0x04
#define RAT_UMTS 0x08
#define RAT_LTE 0x10
#define LTE800 0x00080000 //Band 20
#define LTE1800 0x00000004 //Band 3
#define LTE2600 0x00000040 //Band 7
#define LTE2100 0x00000001 //Band 1
#define GSM1800 0x00000080
#define GSM900E 0x00000100
#define GSM900P 0x00000200
#define GSM850 0x00080000
#define GSM1900 0x00200000
#define UMTS2100 0x00400000
//terminal control
#define SET_RED fprintf(stderr,"\033[1;31;40\155");
#define SET_GREEN fprintf(stderr,"\033[0;32;40\155");
#define SET_WHITE fprintf(stderr,"\033[0;37;40\155");
#define SET_LWHITE fprintf(stderr,"\033[1;37;40\155");
#define SET_DEBUG fprintf(stderr,"\033[1;30;40\155");
#define SET_DBLUE fprintf(stderr,"\033[0;34;40\155");
#define SET_YELLOW fprintf(stderr,"\033[1;33;40\155");
#define SET_CYAN fprintf(stderr,"\033[0;35;40\155");
#define SET_BLUE fprintf(stderr,"\033[0;34;40\155");
#define CURSOR_ON fprintf(stderr,"\033[?25h");
#define CURSOR_OFF fprintf(stderr,"\033[?25l");
#define CLS fprintf(stderr,"\033[2J");
typedef struct qmux
{
uint8_t tf;
uint16_t len;
uint8_t cflags;
uint8_t stype;
uint8_t cid;
}__attribute__((__packed__)) qmx_t;
typedef struct qtrans
{
struct qmux qmh;
uint8_t cflags;
uint16_t tid;
uint16_t msgid;
uint16_t msgsize;
} __attribute__((__packed__)) qmi_t;
typedef struct qtrans_ctl
{
struct qmux qmh;
uint8_t cflags;
uint8_t tid;
uint16_t msgid;
uint16_t msgsize;
} __attribute__((__packed__)) qmictl_t;
struct result_code
{
uint16_t qmi_result;
uint16_t qmi_error;
} __attribute__((__packed__));
int debug = 1;
// default qmi device
char qmi_dev[32];// = "/dev/cdc-wdm0";
// service client id's
uint16_t nas_cid = 0;
// service transaction id's
uint8_t ctl_tid = 1;
uint8_t nas_tid = 1;
uint8_t rbuf[2048];
uint8_t wbuf[512];
int qmi_fd;
struct result_code rc;
static void print_buf(const char* detail,const char* buf,size_t len)
{
int i = 0, z;
int newline = FALSE, indent = FALSE;
char f[512];
unsigned int flen;
snprintf(f,500,"%s (%zu) ",detail,len);
flen = strlen(f);
printf("%s",f);
for (i = 0; i < len; i++)
{
if(indent)
{
z = flen;
while (z--)
printf(" ");
indent = FALSE;
}
printf("%02x ", buf[i]&0xFF);
if(((i + 1) % 32) == 0)
{
printf("\n");
newline = TRUE;
indent = TRUE;
}else{
newline = FALSE;
}
}
if (!newline)
printf("\n");
}
static int tlv_get2(void* msg,uint16_t msgsize,uint8_t tlv_type,void* buf,uint16_t bufsize)
{
int pos;
uint16_t tlv_size = 0;
memset(buf,0x00,bufsize);
for(pos = 0;pos + 3 < msgsize; pos += tlv_size + 3)
{
tlv_size = *(uint16_t*)(msg+pos+1);
if(*(uint8_t*)(msg + pos) == tlv_type)
{
if(bufsize < tlv_size)
return -1;
memcpy(buf,msg+pos+3,tlv_size);
return 0;
}
}
return -1;
}
static int get_result_code(void* msg,uint16_t msgsize)
{
int pos;
uint16_t tlv_size = 0;
uint16_t r[2];
for(pos = 0;pos + 3 < msgsize;pos += tlv_size + 3)
{
tlv_size = *(uint16_t*)(msg+pos+1);
if(*(uint8_t*)(msg+pos) == QMI_TLV_TYPE_RESULT_CODE)
{
memcpy(&r,msg+pos+3,4);
rc.qmi_result = le16toh(r[0]);
rc.qmi_error = le16toh(r[1]);
if(debug > 3)
printf("Result: 0x%04x Error: 0x%04x\n",rc.qmi_result,rc.qmi_error);
return rc.qmi_result;
}
}
}
static int send_msg(void* msg,size_t msglen)
{
ssize_t ret;
if(debug > 2)
{
SET_DBLUE;
print_buf("W ",msg,msglen);
SET_WHITE;
}
ret = write(qmi_fd,msg,msglen);
free(msg);
msg = (void*)NULL;
if(ret != msglen)
{
fprintf(stderr,"Failed to write: wrote %zd err %d\n", ret, errno);
return 0;
}
return ret;
}
static size_t read_msg(int skip_ind,int timewait)
{
ssize_t num;
fd_set in;
int result,i,n,len;
unsigned char c;
qmi_t* resp;
struct stat sb;
struct timeval timeout;
for(;;)
{
timeout.tv_sec = timewait;
timeout.tv_usec = 0;
if(stat(qmi_dev,&sb) == -1)
{
perror("stat");
SET_LWHITE;
exit(EXIT_FAILURE);
}
FD_ZERO(&in);
FD_SET(qmi_fd,&in);
result = select(qmi_fd+1, &in, NULL, NULL, &timeout);
if(result < 1)
return result;
memset(rbuf,2048,0x00);
errno = 0;
for(i = 0,n = 1,len = 2;i <= len && n == 1;i++)
{
n = read(qmi_fd,&c,1);
if(i == 1) len = c;
rbuf[i] = c;
}
//FIXME: How to deal with these server syncs???
if(rbuf[4] == 0x00 && rbuf[8] == 0x27)
{
if(debug > 1)
{
printf("Got QMI CTL Server Sync Indication Message:\n");
if(debug > 2)
{
SET_DEBUG;
print_buf("R ",rbuf,len+1);
SET_WHITE;
}
}
continue;
}
if(skip_ind && rbuf[6] == QMI_MSG_INDICATION)
{
if(debug > 1)
{
resp = (qmi_t*)rbuf;
printf("Got QMI Service Indication Message:\n");
if(debug > 2)
{
SET_DEBUG;
print_buf("R ",rbuf,len+1);
SET_WHITE;
}
}
continue;
}
break;
}
if(debug > 2)
{
SET_DEBUG;
print_buf("R ",rbuf,len+1);
SET_WHITE;
}
return len+1;
}
static int get_cid(uint16_t* cid,uint8_t service_type)
{
size_t rlen;
int err,i;
struct getcid_req
{
struct qtrans_ctl qth;
uint8_t tlv_type;
uint16_t tlv_len;
uint8_t tlv_value;
} __attribute__((__packed__)) *req;
struct qtrans_ctl* resp = (struct qtrans_ctl*)rbuf;
req = (struct getcid_req*)calloc(sizeof(*req),1);
req->qth.qmh.tf = 1;
req->qth.qmh.len = sizeof(*req) - 1;
req->qth.qmh.cflags = 0;
req->qth.qmh.stype = QMI_CTL;
req->qth.qmh.cid = 0x00;
req->qth.cflags = 0x00;
req->qth.tid = ctl_tid++;
req->qth.msgid = QMI_CTL_GET_CLIENT_ID;
req->qth.msgsize = 4;
req->tlv_type = 0x01;
req->tlv_len = 1;
req->tlv_value = service_type;
if(debug > 1)
printf("Running %s...\n",__func__);
send_msg(req,sizeof(*req));
for(i = 0;i < 8;i++)
{
if((rlen = read_msg(TRUE,10)) < 12)
goto errout;
if(resp->msgid != QMI_CTL_GET_CLIENT_ID)
continue;
if(!get_result_code(rbuf+sizeof(struct qtrans_ctl),resp->msgsize))
{
tlv_get2(rbuf+sizeof(struct qtrans_ctl),resp->msgsize,0x01,cid,2);
}else{
SET_RED;
fprintf(stderr,"%s: Request failed! Error Code: 0x%04X\n",__func__,rc.qmi_error);
goto errout;
}
break;
}
if(debug > 2)
printf("CID 0x%04X\n", *cid);
return 0;
errout:
fprintf(stderr,"ERROR: %s failed!\n",__func__);
SET_WHITE;
return -1;
}
static int release_cid(uint16_t cid)
{
size_t rlen;
int err,i;
struct releasecid_req
{
struct qtrans_ctl qth;
uint8_t tlv_type;
uint16_t tlv_len;
uint16_t tlv_value;
} __attribute__((__packed__)) *req;
struct qtrans_ctl* resp = (struct qtrans_ctl*)rbuf;
req = (struct releasecid_req*)calloc(sizeof(*req),1);
req->qth.qmh.tf = 1;
req->qth.qmh.len = sizeof(*req) - 1;
req->qth.qmh.cflags = 0;
req->qth.qmh.stype = QMI_CTL;
req->qth.qmh.cid = 0x00;
req->qth.cflags = 0x00;
req->qth.tid = ctl_tid++;
req->qth.msgid = QMI_CTL_RELEASE_CLIENT_ID;
req->qth.msgsize = 5;
req->tlv_type = 0x01;
req->tlv_len = 2;
req->tlv_value = cid;
if(debug > 1)
printf("Running %s...\n",__func__);
send_msg(req,sizeof(*req));
for(i = 0;i < 4;i++)
{
if((rlen = read_msg(TRUE,10)) < 12)
goto errout;
if(resp->msgid != QMI_CTL_RELEASE_CLIENT_ID)
continue;
if(get_result_code(rbuf+sizeof(struct qtrans_ctl),resp->msgsize))
{
SET_RED;
fprintf(stderr,"Request failed! Error Code: 0x%04X\n",rc.qmi_error);
goto errout;
}
break;
}
return 0;
errout:
fprintf(stderr,"ERROR: release_cid() failed!\n");
SET_WHITE;
return -1;
}
static int abort_nas_req(uint16_t tid)
{
int i,rlen;
struct abort_req
{
qmi_t qth;
uint8_t tlv_type;
uint16_t tlv_len;
uint16_t tlv_value;
}__attribute__((__packed__)) *req;
qmi_t* resp = (qmi_t*)rbuf;
req = (struct abort_req*)calloc(sizeof(*req),1);
req->qth.qmh.tf = 1;
req->qth.qmh.len = sizeof(*req) - 1;
req->qth.qmh.cflags = 0;
req->qth.qmh.stype = QMI_NAS;
req->qth.qmh.cid = nas_cid>>8;
req->qth.cflags = 0x00;
req->qth.tid = nas_tid++;
req->qth.msgid = QMI_NAS_ABORT;
req->qth.msgsize = 0x0005;
req->tlv_type = 0x01;
req->tlv_len = 2;
req->tlv_value = tid;
if(debug > 1)
printf("Running %s...\n",__func__);
send_msg(req,sizeof(*req));
for(i = 0;i < 4;i++)
{
if((rlen = read_msg(TRUE,5)) < 12)
goto errout;
if(resp->msgid != QMI_NAS_ABORT)
continue;
if(get_result_code(rbuf+sizeof(qmi_t),resp->msgsize))
{
fprintf(stderr,"Request failed! Error Code: 0x%04X\n",rc.qmi_error);
goto errout;
}
break;
}
return 0;
errout:
fprintf(stderr,"ERROR: %s failed!\n",__func__);
return -1;
}
static int set_sys_mode(uint8_t rat,uint32_t band,uint32_t lte_band)
{
int i,rlen;
struct sys_select_pref_req
{
qmi_t qth;
uint8_t rat;
uint16_t rat_len;
uint8_t rat_mode[2];
uint8_t band;
uint16_t band_len;
uint8_t rf_band[8];
uint8_t lte_band;
uint16_t lte_band_len;
uint8_t lte_rf_band[8];
uint8_t duration;
uint16_t duration_len;
uint8_t duration_mode;
}__attribute__((__packed__)) *req;
qmi_t* resp = (qmi_t*)rbuf;
req = (struct sys_select_pref_req*)calloc(sizeof(*req),1);
req->qth.qmh.tf = 1;
req->qth.qmh.len = sizeof(*req) - 1;
req->qth.qmh.cflags = 0;
req->qth.qmh.stype = QMI_NAS;
req->qth.qmh.cid = nas_cid>>8;
req->qth.cflags = 0x00;
req->qth.tid = nas_tid++;
req->qth.msgid = QMI_NAS_SET_SYS_SELECTION;
req->qth.msgsize = 31;
req->rat = 0x11;
req->rat_len = 0x0002;
req->rat_mode[0] = rat;
req->rat_mode[1] = 0x00;
req->band = 0x12;
req->band_len = 0x0008;
req->rf_band[0] = band&0xFF;
req->rf_band[1] = (band>>8)&0xFF;
req->rf_band[2] = (band>>16)&0xFF;
req->lte_band = 0x15;
req->lte_band_len = 0x0008;
req->lte_rf_band[0] = lte_band&0xFF;
req->lte_rf_band[2] = (lte_band>>16)&0xFF;
req->duration = 0x17;
req->duration_len = 0x0001;
req->duration_mode = 0x00;
if(debug > 1)
printf("Running %s...\n",__func__);
send_msg(req,sizeof(*req));
for(i = 0;i < 4;i++)
{
if((rlen = read_msg(TRUE,5)) < 12)
goto errout;
if(resp->msgid != QMI_NAS_SET_SYS_SELECTION)
continue;
if(get_result_code(rbuf+sizeof(qmi_t),resp->msgsize))
{
SET_RED;
fprintf(stderr,"Request failed! Error Code: 0x%04X\n",rc.qmi_error);
goto errout;
}
break;
}
return 0;
errout:
fprintf(stderr,"ERROR: set_sys_mode() failed!\n");
SET_WHITE;
return -1;
}
static int set_nas_indication(uint8_t ind,uint8_t on_off)
{
int i,rlen;
struct set_ind_req
{
qmi_t qth;
uint8_t tlv_type;
uint16_t tlv_len;
uint8_t tlv_value;
}__attribute__((__packed__)) *req;
qmi_t* resp = (qmi_t*)rbuf;
req = (struct set_ind_req*)calloc(sizeof(*req),1);
req->qth.qmh.tf = 1;
req->qth.qmh.len = sizeof(*req) - 1;
req->qth.qmh.cflags = 0;
req->qth.qmh.stype = QMI_NAS;
req->qth.qmh.cid = nas_cid>>8;
req->qth.cflags = 0x00;
req->qth.tid = nas_tid++;
req->qth.msgid = QMI_NAS_SET_REG_EVENT;
req->qth.msgsize = 0x0004;
req->tlv_type = ind;
req->tlv_len = 1;
req->tlv_value = on_off;
if(debug > 1)
printf("Running %s...\n",__func__);
send_msg(req,sizeof(*req));
for(i = 0;i < 4;i++)
{
if((rlen = read_msg(TRUE,5)) < 12)
goto errout;
if(resp->msgid != QMI_NAS_SET_REG_EVENT)
continue;
if(get_result_code(rbuf+sizeof(qmi_t),resp->msgsize))
{
fprintf(stderr,"Request failed! Error Code: 0x%04X\n",rc.qmi_error);
goto errout;
}
break;
}
return 0;
errout:
fprintf(stderr,"ERROR: set_nas_indication() failed!\n");
return -1;
}
static int scan_network(uint8_t rat,const char* act)
{
int i,j,k,rlen;
char desc[128];
uint8_t tlv_buf[256];
memset(tlv_buf,0x00,256);
struct scan_req
{
qmi_t qth;
uint8_t type;
uint16_t type_len;
uint8_t type_value;
}__attribute__((__packed__)) *req;
qmi_t* resp = (qmi_t*)rbuf;
req = (struct scan_req*)calloc(sizeof(*req),1);
req->qth.qmh.tf = 1;
req->qth.qmh.len = sizeof(*req) - 1;
req->qth.qmh.cflags = 0;
req->qth.qmh.stype = QMI_NAS;
req->qth.qmh.cid = nas_cid>>8;
req->qth.cflags = 0x00;
req->qth.tid = nas_tid++;
req->qth.msgid = QMI_NAS_SCAN_NETS;
req->qth.msgsize = 0x0004;
req->type = 0x10;
req->type_len = 0x0001;
req->type_value = rat;
if(debug > 1)
printf("Running %s...\n",__func__);
//print_buf(">>>",req,req_len);
send_msg(req,sizeof(*req));
for(i = 0;i < 4;i++)
{
if((rlen = read_msg(TRUE,60)) < 12)
{
if(rlen == 0)
{
fprintf(stderr,"Scan timed out! Abort scan request!\n");
abort_nas_req(req->qth.tid);
goto out;
}
goto errout;
}
if(resp->msgid != QMI_NAS_SCAN_NETS)
continue;
if(!get_result_code(rbuf+sizeof(struct qtrans),resp->msgsize))
{
tlv_get2(rbuf+sizeof(struct qtrans),resp->msgsize,0x10,&tlv_buf,256);
printf("%i network(s) found!\n",tlv_buf[0]);
switch(rat)
{
case 1: SET_GREEN; break;
case 2: SET_BLUE; break;
case 4: SET_CYAN; break;
default: SET_YELLOW; break;
}
for(j = 0,k = 2;j < tlv_buf[0];j++)
{
if(j == 0) printf("\n");
memset(desc,0x00,128);
memcpy(desc,tlv_buf+k+6,tlv_buf[k+5]);
printf("\t%i. -- [%s] MCC:%d MNC:%02d (%s)\n",j+1,act,*(uint16_t*)(tlv_buf+k),tlv_buf[k+2],desc);
k += 6 + tlv_buf[k+5];
if(j == tlv_buf[0]-1) printf("\n");
}
SET_WHITE;
}else{
SET_RED;
fprintf(stderr,"Request failed! Error Code: 0x%04X\n",rc.qmi_error);
goto errout;
}
break;
}
out:
return 0;
errout:
fprintf(stderr,"ERROR: scan_network() failed!\n");
SET_WHITE;
return -1;
}
static void scan_network_bands()
{
fprintf(stderr,"\n\t----- QMI Network Scanner v0.1 -----\n\n");
set_nas_indication(0x13,0); //disable Serving System Events
fprintf(stderr,"Scanning for GSM1800...\t\t ");
set_sys_mode(RAT_GSM,GSM1800,0);
scan_network(0x01,"GSM1800");
fprintf(stderr,"Scanning for GSM900 Extended...\t ");
set_sys_mode(RAT_GSM,GSM900E,0);
scan_network(0x01,"E-GSM900");
fprintf(stderr,"Scanning for GSM900 Primary...\t ");
set_sys_mode(RAT_GSM,GSM900P,0);
scan_network(0x01,"P-GSM900");
fprintf(stderr,"Scanning for UMTS2100...\t ");
set_sys_mode(RAT_UMTS,UMTS2100,0);
scan_network(0x02,"UMTS2100");
fprintf(stderr,"Scanning for LTE800...\t\t ");
set_sys_mode(RAT_LTE,0,LTE800);
scan_network(0x04,"LTE800");
fprintf(stderr,"Scanning for LTE1800...\t\t ");
set_sys_mode(RAT_LTE,0,LTE1800);
scan_network(0x04,"LTE1800");
fprintf(stderr,"Scanning for LTE2600...\t\t ");
set_sys_mode(RAT_LTE,0,LTE2600);
scan_network(0x04,"LTE 2600");
set_sys_mode(RAT_GSM,GSM850,0);
}
static void usage()
{
printf("\nusage: qmi_scanner [-d /dev/cdc-wdmX] [-D] [-h]\n\n");
printf("\t\t-d: qmi control device (defaults to /dev/cdc-wdm0)\n");
printf("\t\t-D: debug level\n");
printf("\t\t-h: show usage\n\n");
exit(0);
}
int main(int argc, char *argv[])
{
int opt;
SET_WHITE;
strncpy(qmi_dev,"/dev/cdc-wdm0",30);
for(;;)
{
opt = getopt(argc, argv, "d:Dh");
if(opt == -1) break;
switch(opt)
{
case 'd': strncpy(qmi_dev,optarg,30); break;
case 'D': debug++; break;
case 'h': usage(); break;
default: break;
}
}
errno = 0;
qmi_fd = open(qmi_dev, O_RDWR | O_EXCL | /*O_NONBLOCK |*/ O_NOCTTY);
if(qmi_fd < 0)
{
SET_RED;
fprintf(stderr,"Failed to open control device %s : %s\n",qmi_dev,strerror(errno));
fprintf(stderr,"Make sure the qmi_wwan module is loaded and your modem has been modeswitched!\n");
SET_LWHITE;
exit(1);
}
while(get_cid(&nas_cid,QMI_NAS) == -1)
sleep(1);
scan_network_bands();
if(nas_cid)
release_cid(nas_cid);
close(qmi_fd);
SET_LWHITE;
return 0;
}