xref: /OK3568_Linux_fs/app/forlinx/quectelCM/device.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun /******************************************************************************
2*4882a593Smuzhiyun   @file    device.c
3*4882a593Smuzhiyun   @brief   QMI device dirver.
4*4882a593Smuzhiyun 
5*4882a593Smuzhiyun   DESCRIPTION
6*4882a593Smuzhiyun   Connectivity Management Tool for USB network adapter of Quectel wireless cellular modules.
7*4882a593Smuzhiyun 
8*4882a593Smuzhiyun   INITIALIZATION AND SEQUENCING REQUIREMENTS
9*4882a593Smuzhiyun   None.
10*4882a593Smuzhiyun 
11*4882a593Smuzhiyun   ---------------------------------------------------------------------------
12*4882a593Smuzhiyun   Copyright (c) 2016 - 2020 Quectel Wireless Solution, Co., Ltd.  All Rights Reserved.
13*4882a593Smuzhiyun   Quectel Wireless Solution Proprietary and Confidential.
14*4882a593Smuzhiyun   ---------------------------------------------------------------------------
15*4882a593Smuzhiyun ******************************************************************************/
16*4882a593Smuzhiyun #include <unistd.h>
17*4882a593Smuzhiyun #include <sys/types.h>
18*4882a593Smuzhiyun #include <fcntl.h>
19*4882a593Smuzhiyun #include <dirent.h>
20*4882a593Smuzhiyun #include <errno.h>
21*4882a593Smuzhiyun #include <strings.h>
22*4882a593Smuzhiyun #include <stdlib.h>
23*4882a593Smuzhiyun #include <limits.h>
24*4882a593Smuzhiyun #include <linux/usbdevice_fs.h>
25*4882a593Smuzhiyun #include <linux/types.h>
26*4882a593Smuzhiyun #include <sys/ioctl.h>
27*4882a593Smuzhiyun #include <sys/socket.h>
28*4882a593Smuzhiyun #include <net/if.h>
29*4882a593Smuzhiyun #include <time.h>
30*4882a593Smuzhiyun #include <pthread.h>
31*4882a593Smuzhiyun 
32*4882a593Smuzhiyun #include "QMIThread.h"
33*4882a593Smuzhiyun #include "ethtool-copy.h"
34*4882a593Smuzhiyun 
35*4882a593Smuzhiyun #define USB_CLASS_VENDOR_SPEC		0xff
36*4882a593Smuzhiyun #define USB_CLASS_COMM			2
37*4882a593Smuzhiyun #define USB_CDC_SUBCLASS_ACM			0x02
38*4882a593Smuzhiyun #define USB_CDC_SUBCLASS_ETHERNET       0x06
39*4882a593Smuzhiyun #define USB_CDC_SUBCLASS_NCM			0x0d
40*4882a593Smuzhiyun #define USB_CDC_SUBCLASS_MBIM			0x0e
41*4882a593Smuzhiyun #define USB_CLASS_WIRELESS_CONTROLLER	0xe0
42*4882a593Smuzhiyun 
43*4882a593Smuzhiyun #define CM_MAX_PATHLEN 256
44*4882a593Smuzhiyun 
45*4882a593Smuzhiyun #define CM_INVALID_VAL (~((int)0))
46*4882a593Smuzhiyun /* get first line from file 'fname'
47*4882a593Smuzhiyun  * And convert the content into a hex number, then return this number */
file_get_value(const char * fname,int base)48*4882a593Smuzhiyun static int file_get_value(const char *fname, int base)
49*4882a593Smuzhiyun {
50*4882a593Smuzhiyun     FILE *fp = NULL;
51*4882a593Smuzhiyun     long num;
52*4882a593Smuzhiyun     char buff[32 + 1] = {'\0'};
53*4882a593Smuzhiyun     char *endptr = NULL;
54*4882a593Smuzhiyun 
55*4882a593Smuzhiyun     fp = fopen(fname, "r");
56*4882a593Smuzhiyun     if (!fp) goto error;
57*4882a593Smuzhiyun     if (fgets(buff, sizeof(buff), fp) == NULL)
58*4882a593Smuzhiyun         goto error;
59*4882a593Smuzhiyun     fclose(fp);
60*4882a593Smuzhiyun 
61*4882a593Smuzhiyun     num = (int)strtol(buff, &endptr, base);
62*4882a593Smuzhiyun     if (errno == ERANGE && (num == LONG_MAX || num == LONG_MIN))
63*4882a593Smuzhiyun         goto error;
64*4882a593Smuzhiyun     /* if there is no digit in buff */
65*4882a593Smuzhiyun     if (endptr == buff)
66*4882a593Smuzhiyun         goto error;
67*4882a593Smuzhiyun 
68*4882a593Smuzhiyun     if (debug_qmi)
69*4882a593Smuzhiyun         dbg_time("(%s) = %lx", fname, num);
70*4882a593Smuzhiyun     return (int)num;
71*4882a593Smuzhiyun 
72*4882a593Smuzhiyun error:
73*4882a593Smuzhiyun     if (fp) fclose(fp);
74*4882a593Smuzhiyun     return CM_INVALID_VAL;
75*4882a593Smuzhiyun }
76*4882a593Smuzhiyun 
77*4882a593Smuzhiyun /*
78*4882a593Smuzhiyun  * This function will search the directory 'dirname' and return the first child.
79*4882a593Smuzhiyun  * '.' and '..' is ignored by default
80*4882a593Smuzhiyun  */
dir_get_child(const char * dirname,char * buff,unsigned bufsize,const char * prefix)81*4882a593Smuzhiyun static int dir_get_child(const char *dirname, char *buff, unsigned bufsize, const char *prefix)
82*4882a593Smuzhiyun {
83*4882a593Smuzhiyun     struct dirent *entptr = NULL;
84*4882a593Smuzhiyun     DIR *dirptr;
85*4882a593Smuzhiyun 
86*4882a593Smuzhiyun     buff[0] = 0;
87*4882a593Smuzhiyun 
88*4882a593Smuzhiyun     dirptr = opendir(dirname);
89*4882a593Smuzhiyun     if (!dirptr)
90*4882a593Smuzhiyun         return -1;
91*4882a593Smuzhiyun 
92*4882a593Smuzhiyun     while ((entptr = readdir(dirptr))) {
93*4882a593Smuzhiyun         if (entptr->d_name[0] == '.')
94*4882a593Smuzhiyun             continue;
95*4882a593Smuzhiyun         if (prefix && strlen(prefix) && strncmp(entptr->d_name, prefix, strlen(prefix)))
96*4882a593Smuzhiyun             continue;
97*4882a593Smuzhiyun         snprintf(buff, bufsize, "%s", entptr->d_name);
98*4882a593Smuzhiyun         break;
99*4882a593Smuzhiyun     }
100*4882a593Smuzhiyun     closedir(dirptr);
101*4882a593Smuzhiyun 
102*4882a593Smuzhiyun     return 0;
103*4882a593Smuzhiyun }
104*4882a593Smuzhiyun 
conf_get_val(const char * fname,const char * key)105*4882a593Smuzhiyun static int conf_get_val(const char *fname, const char *key)
106*4882a593Smuzhiyun {
107*4882a593Smuzhiyun     char buff[128] = {'\0'};
108*4882a593Smuzhiyun     FILE *fp = fopen(fname, "r");
109*4882a593Smuzhiyun     if (!fp)
110*4882a593Smuzhiyun         return CM_INVALID_VAL;
111*4882a593Smuzhiyun 
112*4882a593Smuzhiyun     while (fgets(buff, sizeof(buff)-1, fp)) {
113*4882a593Smuzhiyun         char prefix[128] = {'\0'};
114*4882a593Smuzhiyun         char tail[128] = {'\0'};
115*4882a593Smuzhiyun         /* To eliminate cppcheck warnning: Assume string length is no more than 15 */
116*4882a593Smuzhiyun         sscanf(buff, "%15[^=]=%15s", prefix, tail);
117*4882a593Smuzhiyun         if (!strncasecmp(prefix, key, strlen(key))) {
118*4882a593Smuzhiyun             fclose(fp);
119*4882a593Smuzhiyun             return atoi(tail);
120*4882a593Smuzhiyun         }
121*4882a593Smuzhiyun     }
122*4882a593Smuzhiyun 
123*4882a593Smuzhiyun     fclose(fp);
124*4882a593Smuzhiyun     return CM_INVALID_VAL;
125*4882a593Smuzhiyun }
126*4882a593Smuzhiyun 
query_usb_device_info(char * path,struct usb_device_info * p)127*4882a593Smuzhiyun static void query_usb_device_info(char *path, struct usb_device_info *p) {
128*4882a593Smuzhiyun     size_t offset = strlen(path);
129*4882a593Smuzhiyun 
130*4882a593Smuzhiyun     memset(p, 0, sizeof(*p));
131*4882a593Smuzhiyun 
132*4882a593Smuzhiyun     path[offset] = '\0';
133*4882a593Smuzhiyun     strcat(path, "/idVendor");
134*4882a593Smuzhiyun     p->idVendor = file_get_value(path, 16);
135*4882a593Smuzhiyun 
136*4882a593Smuzhiyun     if (p->idVendor == CM_INVALID_VAL)
137*4882a593Smuzhiyun         return;
138*4882a593Smuzhiyun 
139*4882a593Smuzhiyun     path[offset] = '\0';
140*4882a593Smuzhiyun     strcat(path, "/idProduct");
141*4882a593Smuzhiyun     p->idProduct = file_get_value(path, 16);
142*4882a593Smuzhiyun 
143*4882a593Smuzhiyun     path[offset] = '\0';
144*4882a593Smuzhiyun     strcat(path, "/busnum");
145*4882a593Smuzhiyun     p->busnum = file_get_value(path, 10);
146*4882a593Smuzhiyun 
147*4882a593Smuzhiyun     path[offset] = '\0';
148*4882a593Smuzhiyun     strcat(path, "/devnum");
149*4882a593Smuzhiyun     p->devnum = file_get_value(path, 10);
150*4882a593Smuzhiyun 
151*4882a593Smuzhiyun     path[offset] = '\0';
152*4882a593Smuzhiyun     strcat(path, "/bNumInterfaces");
153*4882a593Smuzhiyun     p->bNumInterfaces = file_get_value(path, 10);
154*4882a593Smuzhiyun 
155*4882a593Smuzhiyun     path[offset] = '\0';
156*4882a593Smuzhiyun }
157*4882a593Smuzhiyun 
query_usb_interface_info(char * path,struct usb_interface_info * p)158*4882a593Smuzhiyun static void query_usb_interface_info(char *path, struct usb_interface_info *p) {
159*4882a593Smuzhiyun     char driver[128];
160*4882a593Smuzhiyun     size_t offset = strlen(path);
161*4882a593Smuzhiyun     int n;
162*4882a593Smuzhiyun 
163*4882a593Smuzhiyun     memset(p, 0, sizeof(*p));
164*4882a593Smuzhiyun 
165*4882a593Smuzhiyun     path[offset] = '\0';
166*4882a593Smuzhiyun     strcat(path, "/bNumEndpoints");
167*4882a593Smuzhiyun     p->bInterfaceClass = file_get_value(path, 16);
168*4882a593Smuzhiyun 
169*4882a593Smuzhiyun     path[offset] = '\0';
170*4882a593Smuzhiyun     strcat(path, "/bInterfaceClass");
171*4882a593Smuzhiyun     p->bInterfaceClass = file_get_value(path, 16);
172*4882a593Smuzhiyun 
173*4882a593Smuzhiyun     path[offset] = '\0';
174*4882a593Smuzhiyun     strcat(path, "/bInterfaceSubClass");
175*4882a593Smuzhiyun     p->bInterfaceSubClass = file_get_value(path, 16);
176*4882a593Smuzhiyun 
177*4882a593Smuzhiyun     path[offset] = '\0';
178*4882a593Smuzhiyun     strcat(path, "/bInterfaceProtocol");
179*4882a593Smuzhiyun     p->bInterfaceProtocol = file_get_value(path, 16);
180*4882a593Smuzhiyun 
181*4882a593Smuzhiyun     path[offset] = '\0';
182*4882a593Smuzhiyun     strcat(path, "/driver");
183*4882a593Smuzhiyun     n = readlink(path, driver, sizeof(driver));
184*4882a593Smuzhiyun     if (n > 0) {
185*4882a593Smuzhiyun         driver[n] = 0;
186*4882a593Smuzhiyun         if (debug_qmi) dbg_time("driver -> %s", driver);
187*4882a593Smuzhiyun         n = strlen(driver);
188*4882a593Smuzhiyun         while (n > 0) {
189*4882a593Smuzhiyun             if (driver[n] == '/')
190*4882a593Smuzhiyun                 break;
191*4882a593Smuzhiyun             n--;
192*4882a593Smuzhiyun         }
193*4882a593Smuzhiyun         strncpy(p->driver, &driver[n+1], sizeof(p->driver));
194*4882a593Smuzhiyun     }
195*4882a593Smuzhiyun 
196*4882a593Smuzhiyun     path[offset] = '\0';
197*4882a593Smuzhiyun }
198*4882a593Smuzhiyun 
detect_path_cdc_wdm_or_qcqmi(char * path,char * devname,size_t bufsize)199*4882a593Smuzhiyun static int detect_path_cdc_wdm_or_qcqmi(char *path, char *devname, size_t bufsize)
200*4882a593Smuzhiyun {
201*4882a593Smuzhiyun     size_t offset = strlen(path);
202*4882a593Smuzhiyun     char tmp[32];
203*4882a593Smuzhiyun 
204*4882a593Smuzhiyun     devname[0] = 0;
205*4882a593Smuzhiyun 
206*4882a593Smuzhiyun     if (access(path, R_OK))
207*4882a593Smuzhiyun         return -1;
208*4882a593Smuzhiyun 
209*4882a593Smuzhiyun     path[offset] = '\0';
210*4882a593Smuzhiyun     strcat(path, "/GobiQMI");
211*4882a593Smuzhiyun     if (!access(path, R_OK))
212*4882a593Smuzhiyun         goto step_1;
213*4882a593Smuzhiyun 
214*4882a593Smuzhiyun     path[offset] = '\0';
215*4882a593Smuzhiyun     strcat(path, "/usbmisc");
216*4882a593Smuzhiyun     if (!access(path, R_OK))
217*4882a593Smuzhiyun         goto step_1;
218*4882a593Smuzhiyun 
219*4882a593Smuzhiyun     path[offset] = '\0';
220*4882a593Smuzhiyun     strcat(path, "/usb");
221*4882a593Smuzhiyun     if (!access(path, R_OK))
222*4882a593Smuzhiyun         goto step_1;
223*4882a593Smuzhiyun 
224*4882a593Smuzhiyun     return -1;
225*4882a593Smuzhiyun 
226*4882a593Smuzhiyun step_1:
227*4882a593Smuzhiyun     /* get device(qcqmiX|cdc-wdmX) */
228*4882a593Smuzhiyun     if (debug_qmi) dbg_time("%s", path);
229*4882a593Smuzhiyun     dir_get_child(path, tmp, sizeof(tmp), NULL);
230*4882a593Smuzhiyun     if (tmp[0] == '\0')
231*4882a593Smuzhiyun         return -1;
232*4882a593Smuzhiyun 
233*4882a593Smuzhiyun     /* There is a chance that, no device(qcqmiX|cdc-wdmX) is generated. We should warn user about that! */
234*4882a593Smuzhiyun     snprintf(devname, bufsize, "/dev/%s", tmp);
235*4882a593Smuzhiyun     if (access(devname, R_OK | F_OK) && errno == ENOENT)
236*4882a593Smuzhiyun     {
237*4882a593Smuzhiyun         int major, minor;
238*4882a593Smuzhiyun 
239*4882a593Smuzhiyun         dbg_time("access %s  failed, errno: %d (%s)", devname, errno, strerror(errno));
240*4882a593Smuzhiyun         strcat(path, "/");
241*4882a593Smuzhiyun         strcat(path, tmp);
242*4882a593Smuzhiyun         strcat(path, "/uevent");
243*4882a593Smuzhiyun         major = conf_get_val(path, "MAJOR");
244*4882a593Smuzhiyun         minor = conf_get_val(path, "MINOR");
245*4882a593Smuzhiyun 
246*4882a593Smuzhiyun         if(major == CM_INVALID_VAL || minor == CM_INVALID_VAL)
247*4882a593Smuzhiyun             dbg_time("get major and minor failed");
248*4882a593Smuzhiyun          else if (mknod(devname, S_IFCHR|0666, (((major & 0xfff) << 8) | (minor & 0xff) | ((minor & 0xfff00) << 12))))
249*4882a593Smuzhiyun             dbg_time("please mknod %s c %d %d", devname, major, minor);
250*4882a593Smuzhiyun     }
251*4882a593Smuzhiyun 
252*4882a593Smuzhiyun     return 0;
253*4882a593Smuzhiyun }
254*4882a593Smuzhiyun 
255*4882a593Smuzhiyun /* To detect the device info of the modem.
256*4882a593Smuzhiyun  * return:
257*4882a593Smuzhiyun  *  FALSE -> fail
258*4882a593Smuzhiyun  *  TRUE -> ok
259*4882a593Smuzhiyun  */
qmidevice_detect(char * qmichannel,char * usbnet_adapter,unsigned bufsize,PROFILE_T * profile)260*4882a593Smuzhiyun BOOL qmidevice_detect(char *qmichannel, char *usbnet_adapter, unsigned bufsize, PROFILE_T *profile) {
261*4882a593Smuzhiyun     struct dirent* ent = NULL;
262*4882a593Smuzhiyun     DIR *pDir;
263*4882a593Smuzhiyun     const char *rootdir = "/sys/bus/usb/devices";
264*4882a593Smuzhiyun     struct {
265*4882a593Smuzhiyun         char path[255*2];
266*4882a593Smuzhiyun     } *pl;
267*4882a593Smuzhiyun     pl = (typeof(pl)) malloc(sizeof(*pl));
268*4882a593Smuzhiyun     memset(pl, 0x00, sizeof(*pl));
269*4882a593Smuzhiyun 
270*4882a593Smuzhiyun     pDir = opendir(rootdir);
271*4882a593Smuzhiyun     if (!pDir) {
272*4882a593Smuzhiyun         dbg_time("opendir %s failed: %s", rootdir, strerror(errno));
273*4882a593Smuzhiyun         goto error;
274*4882a593Smuzhiyun     }
275*4882a593Smuzhiyun 
276*4882a593Smuzhiyun     while ((ent = readdir(pDir)) != NULL)  {
277*4882a593Smuzhiyun         char netcard[32+1] = {'\0'};
278*4882a593Smuzhiyun         char devname[32+5] = {'\0'}; //+strlen("/dev/")
279*4882a593Smuzhiyun         int netIntf;
280*4882a593Smuzhiyun         int driver_type;
281*4882a593Smuzhiyun 
282*4882a593Smuzhiyun         if (ent->d_name[0] == 'u')
283*4882a593Smuzhiyun             continue;
284*4882a593Smuzhiyun 
285*4882a593Smuzhiyun         snprintf(pl->path, sizeof(pl->path), "%s/%s", rootdir, ent->d_name);
286*4882a593Smuzhiyun         query_usb_device_info(pl->path, &profile->usb_dev);
287*4882a593Smuzhiyun         if (profile->usb_dev.idVendor == CM_INVALID_VAL)
288*4882a593Smuzhiyun             continue;
289*4882a593Smuzhiyun 
290*4882a593Smuzhiyun         if (profile->usb_dev.idVendor == 0x2c7c || profile->usb_dev.idVendor == 0x05c6) {
291*4882a593Smuzhiyun             dbg_time("Find %s/%s idVendor=0x%x idProduct=0x%x, bus=0x%03x, dev=0x%03x",
292*4882a593Smuzhiyun                 rootdir, ent->d_name, profile->usb_dev.idVendor, profile->usb_dev.idProduct,
293*4882a593Smuzhiyun                 profile->usb_dev.busnum, profile->usb_dev.devnum);
294*4882a593Smuzhiyun         }
295*4882a593Smuzhiyun 
296*4882a593Smuzhiyun         /* get network interface */
297*4882a593Smuzhiyun         /* NOTICE: there is a case that, bNumberInterface=6, but the net interface is 8 */
298*4882a593Smuzhiyun         /* toolchain-mips_24kc_gcc-5.4.0_musl donot support GLOB_BRACE */
299*4882a593Smuzhiyun         /* RG500U's MBIM is at inteface 0 */
300*4882a593Smuzhiyun         for (netIntf = 0;  netIntf < (profile->usb_dev.bNumInterfaces + 8); netIntf++) {
301*4882a593Smuzhiyun             snprintf(pl->path, sizeof(pl->path), "%s/%s:1.%d/net", rootdir, ent->d_name, netIntf);
302*4882a593Smuzhiyun             dir_get_child(pl->path, netcard, sizeof(netcard), NULL);
303*4882a593Smuzhiyun             if (netcard[0])
304*4882a593Smuzhiyun                 break;
305*4882a593Smuzhiyun         }
306*4882a593Smuzhiyun 
307*4882a593Smuzhiyun         if (netcard[0] == '\0') { //for centos 2.6.x
308*4882a593Smuzhiyun             const char *n= "usb0";
309*4882a593Smuzhiyun             const char *c = "qcqmi0";
310*4882a593Smuzhiyun 
311*4882a593Smuzhiyun             snprintf(pl->path, sizeof(pl->path), "%s/%s:1.4/net:%s", rootdir, ent->d_name, n);
312*4882a593Smuzhiyun             if (!access(pl->path, F_OK)) {
313*4882a593Smuzhiyun                 snprintf(pl->path, sizeof(pl->path), "%s/%s:1.4/GobiQMI:%s", rootdir, ent->d_name, c);
314*4882a593Smuzhiyun                 if (!access(pl->path, F_OK)) {
315*4882a593Smuzhiyun                     snprintf(qmichannel, bufsize, "/dev/%s", c);
316*4882a593Smuzhiyun                     snprintf(usbnet_adapter, bufsize, "%s", n);
317*4882a593Smuzhiyun                     snprintf(pl->path, sizeof(pl->path), "%s/%s:1.4", rootdir, ent->d_name);
318*4882a593Smuzhiyun                     query_usb_interface_info(pl->path, &profile->usb_intf);
319*4882a593Smuzhiyun                     break;
320*4882a593Smuzhiyun                 }
321*4882a593Smuzhiyun             }
322*4882a593Smuzhiyun         }
323*4882a593Smuzhiyun 
324*4882a593Smuzhiyun         if (netcard[0] == '\0')
325*4882a593Smuzhiyun             continue;
326*4882a593Smuzhiyun 
327*4882a593Smuzhiyun         /* not '-i iface' */
328*4882a593Smuzhiyun         if (usbnet_adapter[0] && strcmp(usbnet_adapter, netcard))
329*4882a593Smuzhiyun             continue;
330*4882a593Smuzhiyun 
331*4882a593Smuzhiyun         snprintf(pl->path, sizeof(pl->path), "%s/%s:1.%d", rootdir, ent->d_name, netIntf);
332*4882a593Smuzhiyun         query_usb_interface_info(pl->path, &profile->usb_intf);
333*4882a593Smuzhiyun         driver_type = get_driver_type(profile);
334*4882a593Smuzhiyun 
335*4882a593Smuzhiyun         if (driver_type == SOFTWARE_QMI || driver_type == SOFTWARE_MBIM) {
336*4882a593Smuzhiyun             detect_path_cdc_wdm_or_qcqmi(pl->path, devname, sizeof(devname));
337*4882a593Smuzhiyun         }
338*4882a593Smuzhiyun         else if (driver_type == SOFTWARE_ECM_RNDIS_NCM)
339*4882a593Smuzhiyun         {
340*4882a593Smuzhiyun             int atIntf = -1;
341*4882a593Smuzhiyun 
342*4882a593Smuzhiyun             if (profile->usb_dev.idVendor == 0x2c7c) { //Quectel
343*4882a593Smuzhiyun                 if ((profile->usb_dev.idProduct&0xFF00) == 0x0900) //unisoc
344*4882a593Smuzhiyun                     atIntf = 4;
345*4882a593Smuzhiyun                 else if ((profile->usb_dev.idProduct&0xF000) == 0x8000) //hisi
346*4882a593Smuzhiyun                     atIntf = 4;
347*4882a593Smuzhiyun                 else if ((profile->usb_dev.idProduct&0xFF00) == 0x6000) //asr
348*4882a593Smuzhiyun                     atIntf = 3;
349*4882a593Smuzhiyun                 //else if ((profile->usb_dev.idProduct&0xF000) == 0x0000) //mdm
350*4882a593Smuzhiyun                 //    atIntf = 2;
351*4882a593Smuzhiyun             }
352*4882a593Smuzhiyun 
353*4882a593Smuzhiyun             if (atIntf != -1) {
354*4882a593Smuzhiyun                 snprintf(pl->path, sizeof(pl->path), "%s/%s:1.%d", rootdir, ent->d_name, atIntf);
355*4882a593Smuzhiyun                 dir_get_child(pl->path, devname, sizeof(devname), "tty");
356*4882a593Smuzhiyun                 if (devname[0] && !strcmp(devname, "tty")) {
357*4882a593Smuzhiyun                     snprintf(pl->path, sizeof(pl->path), "%s/%s:1.%d/tty", rootdir, ent->d_name, atIntf);
358*4882a593Smuzhiyun                     dir_get_child(pl->path, devname, sizeof(devname), "tty");
359*4882a593Smuzhiyun                 }
360*4882a593Smuzhiyun             }
361*4882a593Smuzhiyun         }
362*4882a593Smuzhiyun 
363*4882a593Smuzhiyun         if (netcard[0] && devname[0]) {
364*4882a593Smuzhiyun             if (devname[0] == '/')
365*4882a593Smuzhiyun                 snprintf(qmichannel, bufsize, "%s", devname);
366*4882a593Smuzhiyun             else
367*4882a593Smuzhiyun                 snprintf(qmichannel, bufsize, "/dev/%s", devname);
368*4882a593Smuzhiyun             snprintf(usbnet_adapter, bufsize, "%s", netcard);
369*4882a593Smuzhiyun             dbg_time("Auto find qmichannel = %s", qmichannel);
370*4882a593Smuzhiyun             dbg_time("Auto find usbnet_adapter = %s", usbnet_adapter);
371*4882a593Smuzhiyun             break;
372*4882a593Smuzhiyun         }
373*4882a593Smuzhiyun     }
374*4882a593Smuzhiyun     closedir(pDir);
375*4882a593Smuzhiyun 
376*4882a593Smuzhiyun     if (qmichannel[0] == '\0' || usbnet_adapter[0] == '\0') {
377*4882a593Smuzhiyun         dbg_time("network interface '%s' or qmidev '%s' is not exist", usbnet_adapter, qmichannel);
378*4882a593Smuzhiyun         goto error;
379*4882a593Smuzhiyun     }
380*4882a593Smuzhiyun     free(pl);
381*4882a593Smuzhiyun     return TRUE;
382*4882a593Smuzhiyun error:
383*4882a593Smuzhiyun     free(pl);
384*4882a593Smuzhiyun     return FALSE;
385*4882a593Smuzhiyun }
386*4882a593Smuzhiyun 
mhidevice_detect(char * qmichannel,char * usbnet_adapter,PROFILE_T * profile)387*4882a593Smuzhiyun int mhidevice_detect(char *qmichannel, char *usbnet_adapter, PROFILE_T *profile) {
388*4882a593Smuzhiyun     if (!access("/sys/class/net/pcie_mhi0", F_OK))
389*4882a593Smuzhiyun         strcpy(usbnet_adapter, "pcie_mhi0");
390*4882a593Smuzhiyun     else if (!access("/sys/class/net/rmnet_mhi0", F_OK))
391*4882a593Smuzhiyun         strcpy(usbnet_adapter, "rmnet_mhi0");
392*4882a593Smuzhiyun     else {
393*4882a593Smuzhiyun         dbg_time("qmidevice_detect failed");
394*4882a593Smuzhiyun         goto error;
395*4882a593Smuzhiyun     }
396*4882a593Smuzhiyun 
397*4882a593Smuzhiyun     if (!access("/dev/mhi_MBIM", F_OK)) {
398*4882a593Smuzhiyun         strcpy(qmichannel, "/dev/mhi_MBIM");
399*4882a593Smuzhiyun         profile->software_interface = SOFTWARE_MBIM;
400*4882a593Smuzhiyun     }
401*4882a593Smuzhiyun     else if (!access("/dev/mhi_QMI0", F_OK)) {
402*4882a593Smuzhiyun         strcpy(qmichannel, "/dev/mhi_QMI0");
403*4882a593Smuzhiyun         profile->software_interface = SOFTWARE_QMI;
404*4882a593Smuzhiyun     }
405*4882a593Smuzhiyun     else {
406*4882a593Smuzhiyun         goto error;
407*4882a593Smuzhiyun     }
408*4882a593Smuzhiyun 
409*4882a593Smuzhiyun     return 1;
410*4882a593Smuzhiyun error:
411*4882a593Smuzhiyun     return 0;
412*4882a593Smuzhiyun }
413*4882a593Smuzhiyun 
get_driver_type(PROFILE_T * profile)414*4882a593Smuzhiyun int get_driver_type(PROFILE_T *profile)
415*4882a593Smuzhiyun {
416*4882a593Smuzhiyun     /* QMI_WWAN */
417*4882a593Smuzhiyun     if (profile->usb_intf.bInterfaceClass == USB_CLASS_VENDOR_SPEC) {
418*4882a593Smuzhiyun         return SOFTWARE_QMI;
419*4882a593Smuzhiyun     }
420*4882a593Smuzhiyun     else if (profile->usb_intf.bInterfaceClass == USB_CLASS_COMM) {
421*4882a593Smuzhiyun         switch (profile->usb_intf.bInterfaceSubClass) {
422*4882a593Smuzhiyun             case USB_CDC_SUBCLASS_MBIM:
423*4882a593Smuzhiyun                 return SOFTWARE_MBIM;
424*4882a593Smuzhiyun             break;
425*4882a593Smuzhiyun             case USB_CDC_SUBCLASS_ETHERNET:
426*4882a593Smuzhiyun             case USB_CDC_SUBCLASS_NCM:
427*4882a593Smuzhiyun                 return SOFTWARE_ECM_RNDIS_NCM;
428*4882a593Smuzhiyun             break;
429*4882a593Smuzhiyun             default:
430*4882a593Smuzhiyun             break;
431*4882a593Smuzhiyun         }
432*4882a593Smuzhiyun     }
433*4882a593Smuzhiyun     else if (profile->usb_intf.bInterfaceClass == USB_CLASS_WIRELESS_CONTROLLER) {
434*4882a593Smuzhiyun         if (profile->usb_intf.bInterfaceSubClass == 1 && profile->usb_intf.bInterfaceProtocol == 3)
435*4882a593Smuzhiyun             return SOFTWARE_ECM_RNDIS_NCM;
436*4882a593Smuzhiyun     }
437*4882a593Smuzhiyun 
438*4882a593Smuzhiyun     dbg_time("%s unknow bInterfaceClass=%d, bInterfaceSubClass=%d", __func__,
439*4882a593Smuzhiyun         profile->usb_intf.bInterfaceClass, profile->usb_intf.bInterfaceSubClass);
440*4882a593Smuzhiyun     return DRV_INVALID;
441*4882a593Smuzhiyun }
442*4882a593Smuzhiyun 
443*4882a593Smuzhiyun struct usbfs_getdriver
444*4882a593Smuzhiyun {
445*4882a593Smuzhiyun     unsigned int interface;
446*4882a593Smuzhiyun     char driver[255 + 1];
447*4882a593Smuzhiyun };
448*4882a593Smuzhiyun 
449*4882a593Smuzhiyun struct usbfs_ioctl
450*4882a593Smuzhiyun {
451*4882a593Smuzhiyun     int ifno;       /* interface 0..N ; negative numbers reserved */
452*4882a593Smuzhiyun     int ioctl_code; /* MUST encode size + direction of data so the
453*4882a593Smuzhiyun 			 * macros in <asm/ioctl.h> give correct values */
454*4882a593Smuzhiyun     void *data;     /* param buffer (in, or out) */
455*4882a593Smuzhiyun };
456*4882a593Smuzhiyun 
457*4882a593Smuzhiyun #define IOCTL_USBFS_DISCONNECT	_IO('U', 22)
458*4882a593Smuzhiyun #define IOCTL_USBFS_CONNECT	_IO('U', 23)
459*4882a593Smuzhiyun 
usbfs_is_kernel_driver_alive(int fd,int ifnum)460*4882a593Smuzhiyun int usbfs_is_kernel_driver_alive(int fd, int ifnum)
461*4882a593Smuzhiyun {
462*4882a593Smuzhiyun     struct usbfs_getdriver getdrv;
463*4882a593Smuzhiyun     getdrv.interface = ifnum;
464*4882a593Smuzhiyun     if (ioctl(fd, USBDEVFS_GETDRIVER, &getdrv) < 0) {
465*4882a593Smuzhiyun         dbg_time("%s ioctl USBDEVFS_GETDRIVER failed, kernel driver may be inactive", __func__);
466*4882a593Smuzhiyun         return 0;
467*4882a593Smuzhiyun     }
468*4882a593Smuzhiyun     dbg_time("%s find interface %d has match the driver %s", __func__, ifnum, getdrv.driver);
469*4882a593Smuzhiyun     return 1;
470*4882a593Smuzhiyun }
471*4882a593Smuzhiyun 
usbfs_detach_kernel_driver(int fd,int ifnum)472*4882a593Smuzhiyun void usbfs_detach_kernel_driver(int fd, int ifnum)
473*4882a593Smuzhiyun {
474*4882a593Smuzhiyun     struct usbfs_ioctl operate;
475*4882a593Smuzhiyun     operate.data = NULL;
476*4882a593Smuzhiyun     operate.ifno = ifnum;
477*4882a593Smuzhiyun     operate.ioctl_code = IOCTL_USBFS_DISCONNECT;
478*4882a593Smuzhiyun     if (ioctl(fd, USBDEVFS_IOCTL, &operate) < 0) {
479*4882a593Smuzhiyun         dbg_time("%s detach kernel driver failed", __func__);
480*4882a593Smuzhiyun     } else {
481*4882a593Smuzhiyun         dbg_time("%s detach kernel driver success", __func__);
482*4882a593Smuzhiyun     }
483*4882a593Smuzhiyun }
484*4882a593Smuzhiyun 
usbfs_attach_kernel_driver(int fd,int ifnum)485*4882a593Smuzhiyun void usbfs_attach_kernel_driver(int fd, int ifnum)
486*4882a593Smuzhiyun {
487*4882a593Smuzhiyun     struct usbfs_ioctl operate;
488*4882a593Smuzhiyun     operate.data = NULL;
489*4882a593Smuzhiyun     operate.ifno = ifnum;
490*4882a593Smuzhiyun     operate.ioctl_code = IOCTL_USBFS_CONNECT;
491*4882a593Smuzhiyun     if (ioctl(fd, USBDEVFS_IOCTL, &operate) < 0) {
492*4882a593Smuzhiyun         dbg_time("%s detach kernel driver failed", __func__);
493*4882a593Smuzhiyun     } else {
494*4882a593Smuzhiyun         dbg_time("%s detach kernel driver success", __func__);
495*4882a593Smuzhiyun     }
496*4882a593Smuzhiyun }
497*4882a593Smuzhiyun 
reattach_driver(PROFILE_T * profile)498*4882a593Smuzhiyun int reattach_driver(PROFILE_T *profile)
499*4882a593Smuzhiyun {
500*4882a593Smuzhiyun     int ifnum = 4;
501*4882a593Smuzhiyun     int fd;
502*4882a593Smuzhiyun     char devpath[128] = {'\0'};
503*4882a593Smuzhiyun     snprintf(devpath, sizeof(devpath), "/dev/bus/usb/%03d/%03d", profile->usb_dev.busnum, profile->usb_dev.devnum);
504*4882a593Smuzhiyun     fd = open(devpath, O_RDWR | O_NOCTTY);
505*4882a593Smuzhiyun     if (fd < 0)
506*4882a593Smuzhiyun     {
507*4882a593Smuzhiyun         dbg_time("%s fail to open %s", __func__, devpath);
508*4882a593Smuzhiyun         return -1;
509*4882a593Smuzhiyun     }
510*4882a593Smuzhiyun     usbfs_detach_kernel_driver(fd, ifnum);
511*4882a593Smuzhiyun     usbfs_attach_kernel_driver(fd, ifnum);
512*4882a593Smuzhiyun 	close(fd);
513*4882a593Smuzhiyun     return 0;
514*4882a593Smuzhiyun }
515*4882a593Smuzhiyun 
516*4882a593Smuzhiyun #define SIOCETHTOOL     0x8946
ql_get_netcard_driver_info(const char * devname)517*4882a593Smuzhiyun int ql_get_netcard_driver_info(const char *devname)
518*4882a593Smuzhiyun {
519*4882a593Smuzhiyun     int fd = -1;
520*4882a593Smuzhiyun     struct ethtool_drvinfo drvinfo;
521*4882a593Smuzhiyun     struct ifreq ifr;	/* ifreq suitable for ethtool ioctl */
522*4882a593Smuzhiyun 
523*4882a593Smuzhiyun     memset(&ifr, 0, sizeof(ifr));
524*4882a593Smuzhiyun     strcpy(ifr.ifr_name, devname);
525*4882a593Smuzhiyun 
526*4882a593Smuzhiyun     fd = socket(AF_INET, SOCK_DGRAM, 0);
527*4882a593Smuzhiyun     if (fd < 0) {
528*4882a593Smuzhiyun         dbg_time("Cannot get control socket: errno(%d)(%s)", errno, strerror(errno));
529*4882a593Smuzhiyun         return -1;
530*4882a593Smuzhiyun     }
531*4882a593Smuzhiyun 
532*4882a593Smuzhiyun     drvinfo.cmd = ETHTOOL_GDRVINFO;
533*4882a593Smuzhiyun     ifr.ifr_data = (void *)&drvinfo;
534*4882a593Smuzhiyun 
535*4882a593Smuzhiyun     if (ioctl(fd, SIOCETHTOOL, &ifr) < 0) {
536*4882a593Smuzhiyun         dbg_time("ioctl() error: errno(%d)(%s)", errno, strerror(errno));
537*4882a593Smuzhiyun         return -1;
538*4882a593Smuzhiyun     }
539*4882a593Smuzhiyun 
540*4882a593Smuzhiyun     dbg_time("netcard driver = %s, driver version = %s", drvinfo.driver, drvinfo.version);
541*4882a593Smuzhiyun 
542*4882a593Smuzhiyun 	close(fd);
543*4882a593Smuzhiyun 
544*4882a593Smuzhiyun     return 0;
545*4882a593Smuzhiyun }
546*4882a593Smuzhiyun 
catch_log(void * arg)547*4882a593Smuzhiyun static void *catch_log(void *arg)
548*4882a593Smuzhiyun {
549*4882a593Smuzhiyun     PROFILE_T *profile = (PROFILE_T *)arg;
550*4882a593Smuzhiyun     int nreads = 0;
551*4882a593Smuzhiyun     char tbuff[256+32];
552*4882a593Smuzhiyun     char filter[32];
553*4882a593Smuzhiyun     size_t tsize = strlen(get_time()) + 1;
554*4882a593Smuzhiyun 
555*4882a593Smuzhiyun     snprintf(filter, sizeof(filter), ":%d:%03d:", profile->usb_dev.busnum, profile->usb_dev.devnum);
556*4882a593Smuzhiyun 
557*4882a593Smuzhiyun     while(1) {
558*4882a593Smuzhiyun         nreads = read(profile->usbmon_fd, tbuff + tsize, sizeof(tbuff) - tsize - 1);
559*4882a593Smuzhiyun         if (nreads <= 0) {
560*4882a593Smuzhiyun             if (nreads == -1 && errno == EINTR)
561*4882a593Smuzhiyun                 continue;
562*4882a593Smuzhiyun             break;
563*4882a593Smuzhiyun         }
564*4882a593Smuzhiyun 
565*4882a593Smuzhiyun         tbuff[tsize+nreads] = '\0';   // printf("%s", buff);
566*4882a593Smuzhiyun 
567*4882a593Smuzhiyun         if (!strstr(tbuff+tsize, filter))
568*4882a593Smuzhiyun             continue;
569*4882a593Smuzhiyun 
570*4882a593Smuzhiyun         snprintf(tbuff, sizeof(tbuff), "%s", get_time());
571*4882a593Smuzhiyun         tbuff[tsize-1] = ' ';
572*4882a593Smuzhiyun 
573*4882a593Smuzhiyun         fwrite(tbuff, strlen(tbuff), 1, profile->usbmon_logfile_fp);
574*4882a593Smuzhiyun     }
575*4882a593Smuzhiyun 
576*4882a593Smuzhiyun     return NULL;
577*4882a593Smuzhiyun }
578*4882a593Smuzhiyun 
ql_capture_usbmon_log(PROFILE_T * profile,const char * log_path)579*4882a593Smuzhiyun int ql_capture_usbmon_log(PROFILE_T *profile, const char *log_path)
580*4882a593Smuzhiyun {
581*4882a593Smuzhiyun     char usbmon_path[256];
582*4882a593Smuzhiyun     pthread_t pt;
583*4882a593Smuzhiyun     pthread_attr_t attr;
584*4882a593Smuzhiyun 
585*4882a593Smuzhiyun     if (access("/sys/module/usbmon", F_OK)) {
586*4882a593Smuzhiyun         dbg_time("usbmon is not load, please execute \"modprobe usbmon\" or \"insmod usbmon.ko\"");
587*4882a593Smuzhiyun         return -1;
588*4882a593Smuzhiyun     }
589*4882a593Smuzhiyun 
590*4882a593Smuzhiyun     if (access("/sys/kernel/debug/usb", F_OK)) {
591*4882a593Smuzhiyun         dbg_time("debugfs is not mount, please execute \"mount -t debugfs none_debugs /sys/kernel/debug\"");
592*4882a593Smuzhiyun         return -1;
593*4882a593Smuzhiyun     }
594*4882a593Smuzhiyun 
595*4882a593Smuzhiyun     snprintf(usbmon_path, sizeof(usbmon_path), "/sys/kernel/debug/usb/usbmon/%du", profile->usb_dev.busnum);
596*4882a593Smuzhiyun     profile->usbmon_fd = open(usbmon_path, O_RDONLY);
597*4882a593Smuzhiyun     if (profile->usbmon_fd < 0) {
598*4882a593Smuzhiyun         dbg_time("open %s error(%d) (%s)", usbmon_path, errno, strerror(errno));
599*4882a593Smuzhiyun         return -1;
600*4882a593Smuzhiyun     }
601*4882a593Smuzhiyun 
602*4882a593Smuzhiyun     snprintf(usbmon_path, sizeof(usbmon_path), "cat /sys/kernel/debug/usb/devices >> %s", log_path);
603*4882a593Smuzhiyun     if (system(usbmon_path) == -1) {};
604*4882a593Smuzhiyun 
605*4882a593Smuzhiyun     profile->usbmon_logfile_fp = fopen(log_path, "wb");
606*4882a593Smuzhiyun     if (!profile->usbmon_logfile_fp) {
607*4882a593Smuzhiyun       dbg_time("open %s error(%d) (%s)", log_path, errno, strerror(errno));
608*4882a593Smuzhiyun       close(profile->usbmon_fd);
609*4882a593Smuzhiyun       profile->usbmon_fd = -1;
610*4882a593Smuzhiyun       return -1;
611*4882a593Smuzhiyun     }
612*4882a593Smuzhiyun 
613*4882a593Smuzhiyun     pthread_attr_init(&attr);
614*4882a593Smuzhiyun     pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
615*4882a593Smuzhiyun 
616*4882a593Smuzhiyun     pthread_create(&pt, &attr, catch_log, (void *)profile);
617*4882a593Smuzhiyun 
618*4882a593Smuzhiyun     return 0;
619*4882a593Smuzhiyun }
620*4882a593Smuzhiyun 
ql_stop_usbmon_log(PROFILE_T * profile)621*4882a593Smuzhiyun void ql_stop_usbmon_log(PROFILE_T *profile) {
622*4882a593Smuzhiyun     if (profile->usbmon_fd > 0)
623*4882a593Smuzhiyun         close(profile->usbmon_fd);
624*4882a593Smuzhiyun     if (profile->usbmon_logfile_fp)
625*4882a593Smuzhiyun         fclose(profile->usbmon_logfile_fp);
626*4882a593Smuzhiyun }
627