You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

500 lines
15 KiB

2 years ago
  1. /******************************************************************************
  2. @file device.c
  3. @brief QMI device dirver.
  4. DESCRIPTION
  5. Connectivity Management Tool for USB network adapter of Quectel wireless cellular modules.
  6. INITIALIZATION AND SEQUENCING REQUIREMENTS
  7. None.
  8. ---------------------------------------------------------------------------
  9. Copyright (c) 2016 - 2020 Quectel Wireless Solution, Co., Ltd. All Rights Reserved.
  10. Quectel Wireless Solution Proprietary and Confidential.
  11. ---------------------------------------------------------------------------
  12. ******************************************************************************/
  13. #include <unistd.h>
  14. #include <sys/types.h>
  15. #include <fcntl.h>
  16. #include <dirent.h>
  17. #include <errno.h>
  18. #include <strings.h>
  19. #include <stdlib.h>
  20. #include <limits.h>
  21. #include <linux/usbdevice_fs.h>
  22. #include <linux/types.h>
  23. #include <sys/ioctl.h>
  24. #include <sys/socket.h>
  25. #include <net/if.h>
  26. #include <time.h>
  27. #include <pthread.h>
  28. #include "QMIThread.h"
  29. #include "ethtool-copy.h"
  30. #define CM_MAX_PATHLEN 256
  31. #define CM_INVALID_VAL (~((int)0))
  32. /* get first line from file 'fname'
  33. * And convert the content into a hex number, then return this number */
  34. static int file_get_value(const char *fname, int base)
  35. {
  36. FILE *fp = NULL;
  37. int num;
  38. char buff[32 + 1] = {'\0'};
  39. char *endptr = NULL;
  40. fp = fopen(fname, "r");
  41. if (!fp) goto error;
  42. if (fgets(buff, sizeof(buff), fp) == NULL)
  43. goto error;
  44. fclose(fp);
  45. num = (int)strtol(buff, &endptr, base);
  46. if (errno == ERANGE && (num == LONG_MAX || num == LONG_MIN))
  47. goto error;
  48. /* if there is no digit in buff */
  49. if (endptr == buff)
  50. goto error;
  51. return num;
  52. error:
  53. if (fp) fclose(fp);
  54. return CM_INVALID_VAL;
  55. }
  56. /*
  57. * This function will search the directory 'dirname' and return the first child.
  58. * '.' and '..' is ignored by default
  59. */
  60. int dir_get_child(const char *dirname, char *buff, unsigned bufsize)
  61. {
  62. struct dirent *entptr = NULL;
  63. DIR *dirptr = opendir(dirname);
  64. if (!dirptr)
  65. goto error;
  66. while ((entptr = readdir(dirptr))) {
  67. if (entptr->d_name[0] == '.')
  68. continue;
  69. snprintf(buff, bufsize, "%s", entptr->d_name);
  70. break;
  71. }
  72. closedir(dirptr);
  73. return 0;
  74. error:
  75. buff[0] = '\0';
  76. if (dirptr) closedir(dirptr);
  77. return -1;
  78. }
  79. int conf_get_val(const char *fname, const char *key)
  80. {
  81. char buff[CM_MAX_BUFF] = {'\0'};
  82. FILE *fp = fopen(fname, "r");
  83. if (!fp)
  84. goto error;
  85. while (fgets(buff, CM_MAX_BUFF, fp)) {
  86. char prefix[CM_MAX_BUFF] = {'\0'};
  87. char tail[CM_MAX_BUFF] = {'\0'};
  88. /* To eliminate cppcheck warnning: Assume string length is no more than 15 */
  89. sscanf(buff, "%15[^=]=%15s", prefix, tail);
  90. if (!strncasecmp(prefix, key, strlen(key))) {
  91. fclose(fp);
  92. return atoi(tail);
  93. }
  94. }
  95. error:
  96. fclose(fp);
  97. return CM_INVALID_VAL;
  98. }
  99. static int detect_path_cdc_wdm_or_qcqmi(char *path, size_t len)
  100. {
  101. size_t offset = strlen(path);
  102. if (!access(path, R_OK))
  103. {
  104. path[offset] = '\0';
  105. strcat(path, "/GobiQMI");
  106. if (!access(path, R_OK))
  107. return 0;
  108. path[offset] = '\0';
  109. strcat(path, "/usbmisc");
  110. if (!access(path, R_OK))
  111. return 0;
  112. path[offset] = '\0';
  113. strcat(path, "/usb");
  114. if (!access(path, R_OK))
  115. return 0;
  116. }
  117. return -1;
  118. }
  119. /* To detect the device info of the modem.
  120. * return:
  121. * FALSE -> fail
  122. * TRUE -> ok
  123. */
  124. BOOL qmidevice_detect(char *qmichannel, char *usbnet_adapter, unsigned bufsize, int *pbusnum, int *pdevnum) {
  125. struct dirent* ent = NULL;
  126. DIR *pDir;
  127. const char *rootdir = "/sys/bus/usb/devices";
  128. struct {
  129. char path[255*2];
  130. char uevent[255*3];
  131. } *pl;
  132. pl = (typeof(pl)) malloc(sizeof(*pl));
  133. memset(pl, 0x00, sizeof(*pl));
  134. pDir = opendir(rootdir);
  135. if (!pDir) {
  136. dbg_time("opendir %s failed: %s", rootdir, strerror(errno));
  137. goto error;
  138. }
  139. while ((ent = readdir(pDir)) != NULL) {
  140. int idVendor;
  141. int idProduct;
  142. char netcard[32+1] = {'\0'};
  143. char device[32+1] = {'\0'};
  144. char devname[32+1+6] = {'\0'};
  145. int busnum, devnum;
  146. snprintf(pl->path, sizeof(pl->path), "%s/%s/idVendor", rootdir, ent->d_name);
  147. idVendor = file_get_value(pl->path, 16);
  148. snprintf(pl->path, sizeof(pl->path), "%s/%s/idProduct", rootdir, ent->d_name);
  149. idProduct = file_get_value(pl->path, 16);
  150. if (idVendor != 0x05c6 && idVendor != 0x2c7c)
  151. continue;
  152. snprintf(pl->path, sizeof(pl->path), "%s/%s/busnum", rootdir, ent->d_name);
  153. busnum = file_get_value(pl->path, 10);
  154. snprintf(pl->path, sizeof(pl->path), "%s/%s/devnum", rootdir, ent->d_name);
  155. devnum = file_get_value(pl->path, 10);
  156. dbg_time("Find %s/%s idVendor=0x%x idProduct=0x%x, bus=0x%03x, dev=0x%03x",
  157. rootdir, ent->d_name, idVendor, idProduct, busnum, devnum);
  158. /* get network interface */
  159. snprintf(pl->path, sizeof(pl->path), "%s/%s:1.4/net", rootdir, ent->d_name);
  160. dir_get_child(pl->path, netcard, sizeof(netcard));
  161. if (netcard[0] == '\0') {
  162. snprintf(pl->path, sizeof(pl->path), "%s/%s:1.8/net", rootdir, ent->d_name); //for EM12's MBIM
  163. dir_get_child(pl->path, netcard, sizeof(netcard));
  164. }
  165. if (netcard[0] == '\0') { //for centos 2.6.x
  166. const char *n= "usb0";
  167. const char *c = "qcqmi0";
  168. snprintf(pl->path, sizeof(pl->path), "%s/%s:1.4/net:%s", rootdir, ent->d_name, n);
  169. if (!access(pl->path, F_OK)) {
  170. snprintf(pl->path, sizeof(pl->path), "%s/%s:1.4/GobiQMI:%s", rootdir, ent->d_name, c);
  171. if (!access(pl->path, F_OK)) {
  172. snprintf(qmichannel, bufsize, "/dev/%s", c);
  173. snprintf(usbnet_adapter, bufsize, "%s", n);
  174. break;
  175. }
  176. }
  177. }
  178. if (netcard[0] == '\0')
  179. continue;
  180. if (usbnet_adapter[0] && strcmp(usbnet_adapter, netcard))
  181. continue;
  182. pl->path[strlen(pl->path)-strlen("/net")] = '\0';
  183. if (detect_path_cdc_wdm_or_qcqmi(pl->path, sizeof(pl->path)))
  184. continue;
  185. /* get device */
  186. dir_get_child(pl->path, device, sizeof(device));
  187. if (device[0] == '\0')
  188. continue;
  189. /* There is a chance that, no device(qcqmiX|cdc-wdmX) is generated. We should warn user about that! */
  190. snprintf(devname, sizeof(devname), "/dev/%s", device);
  191. if (access(devname, R_OK | F_OK) && errno == ENOENT) {
  192. int major;
  193. int minor;
  194. int ret;
  195. dbg_time("%s access failed, errno: %d (%s)", devname, errno, strerror(errno));
  196. snprintf(pl->uevent, sizeof(pl->uevent), "%s/%s/uevent", pl->path, device);
  197. major = conf_get_val(pl->uevent, "MAJOR");
  198. minor = conf_get_val(pl->uevent, "MINOR");
  199. if(major == CM_INVALID_VAL || minor == CM_INVALID_VAL)
  200. dbg_time("get major and minor failed");
  201. ret = mknod(devname, S_IFCHR|0666, (((major & 0xfff) << 8) | (minor & 0xff) | ((minor & 0xfff00) << 12)));
  202. if (ret)
  203. dbg_time("please mknod %s c %d %d", devname, major, minor);
  204. }
  205. if (netcard[0] && device[0]) {
  206. snprintf(qmichannel, bufsize, "/dev/%s", device);
  207. snprintf(usbnet_adapter, bufsize, "%s", netcard);
  208. dbg_time("Auto find qmichannel = %s", qmichannel);
  209. dbg_time("Auto find usbnet_adapter = %s", usbnet_adapter);
  210. *pbusnum = busnum;
  211. *pdevnum = devnum;
  212. break;
  213. }
  214. }
  215. closedir(pDir);
  216. if (qmichannel[0] == '\0' || usbnet_adapter[0] == '\0') {
  217. dbg_time("network interface '%s' or qmidev '%s' is not exist", usbnet_adapter, qmichannel);
  218. goto error;
  219. }
  220. free(pl);
  221. return TRUE;
  222. error:
  223. free(pl);
  224. return FALSE;
  225. }
  226. int mhidevice_detect(char *qmichannel, char *usbnet_adapter, PROFILE_T *profile) {
  227. if (!access("/sys/class/net/pcie_mhi0", F_OK))
  228. strcpy(usbnet_adapter, "pcie_mhi0");
  229. else if (!access("/sys/class/net/rmnet_mhi0", F_OK))
  230. strcpy(usbnet_adapter, "rmnet_mhi0");
  231. else {
  232. dbg_time("qmidevice_detect failed");
  233. goto error;
  234. }
  235. if (!access("/dev/mhi_MBIM", F_OK)) {
  236. strcpy(qmichannel, "/dev/mhi_MBIM");
  237. profile->software_interface = SOFTWARE_MBIM;
  238. }
  239. else if (!access("/dev/mhi_QMI0", F_OK)) {
  240. strcpy(qmichannel, "/dev/mhi_QMI0");
  241. profile->software_interface = SOFTWARE_QMI;
  242. }
  243. else {
  244. goto error;
  245. }
  246. return 1;
  247. error:
  248. return 0;
  249. }
  250. #define USB_CLASS_COMM 2
  251. #define USB_CLASS_VENDOR_SPEC 0xff
  252. #define USB_CDC_SUBCLASS_MBIM 0x0e
  253. int get_driver_type(PROFILE_T *profile)
  254. {
  255. char path[CM_MAX_PATHLEN+1] = {'\0'};
  256. int bInterfaceClass;
  257. int type = DRV_INVALID;
  258. snprintf(path, sizeof(path), "/sys/class/net/%s/device/bInterfaceClass", profile->usbnet_adapter);
  259. bInterfaceClass = file_get_value(path, 16);
  260. /* QMI_WWAN */
  261. if (bInterfaceClass == USB_CLASS_VENDOR_SPEC)
  262. type = SOFTWARE_QMI;
  263. /* CDC_MBIM */
  264. if (bInterfaceClass == USB_CLASS_COMM)
  265. type = SOFTWARE_MBIM;
  266. return type;
  267. }
  268. struct usbfs_getdriver
  269. {
  270. unsigned int interface;
  271. char driver[255 + 1];
  272. };
  273. struct usbfs_ioctl
  274. {
  275. int ifno; /* interface 0..N ; negative numbers reserved */
  276. int ioctl_code; /* MUST encode size + direction of data so the
  277. * macros in <asm/ioctl.h> give correct values */
  278. void *data; /* param buffer (in, or out) */
  279. };
  280. #define IOCTL_USBFS_DISCONNECT _IO('U', 22)
  281. #define IOCTL_USBFS_CONNECT _IO('U', 23)
  282. int usbfs_is_kernel_driver_alive(int fd, int ifnum)
  283. {
  284. struct usbfs_getdriver getdrv;
  285. getdrv.interface = ifnum;
  286. if (ioctl(fd, USBDEVFS_GETDRIVER, &getdrv) < 0) {
  287. dbg_time("%s ioctl USBDEVFS_GETDRIVER failed, kernel driver may be inactive", __func__);
  288. return 0;
  289. }
  290. dbg_time("%s find interface %d has match the driver %s", __func__, ifnum, getdrv.driver);
  291. return 1;
  292. }
  293. void usbfs_detach_kernel_driver(int fd, int ifnum)
  294. {
  295. struct usbfs_ioctl operate;
  296. operate.data = NULL;
  297. operate.ifno = ifnum;
  298. operate.ioctl_code = IOCTL_USBFS_DISCONNECT;
  299. if (ioctl(fd, USBDEVFS_IOCTL, &operate) < 0) {
  300. dbg_time("%s detach kernel driver failed", __func__);
  301. } else {
  302. dbg_time("%s detach kernel driver success", __func__);
  303. }
  304. }
  305. void usbfs_attach_kernel_driver(int fd, int ifnum)
  306. {
  307. struct usbfs_ioctl operate;
  308. operate.data = NULL;
  309. operate.ifno = ifnum;
  310. operate.ioctl_code = IOCTL_USBFS_CONNECT;
  311. if (ioctl(fd, USBDEVFS_IOCTL, &operate) < 0) {
  312. dbg_time("%s detach kernel driver failed", __func__);
  313. } else {
  314. dbg_time("%s detach kernel driver success", __func__);
  315. }
  316. }
  317. int reattach_driver(PROFILE_T *profile)
  318. {
  319. int ifnum = 4;
  320. int fd;
  321. char devpath[128] = {'\0'};
  322. snprintf(devpath, sizeof(devpath), "/dev/bus/usb/%03d/%03d", profile->busnum, profile->devnum);
  323. fd = open(devpath, O_RDWR | O_NOCTTY);
  324. if (fd < 0)
  325. {
  326. dbg_time("%s fail to open %s", __func__, devpath);
  327. return -1;
  328. }
  329. usbfs_detach_kernel_driver(fd, ifnum);
  330. usbfs_attach_kernel_driver(fd, ifnum);
  331. close(fd);
  332. return 0;
  333. }
  334. #define SIOCETHTOOL 0x8946
  335. int ql_get_netcard_driver_info(const char *devname)
  336. {
  337. int fd = -1;
  338. struct ethtool_drvinfo drvinfo;
  339. struct ifreq ifr; /* ifreq suitable for ethtool ioctl */
  340. memset(&ifr, 0, sizeof(ifr));
  341. strcpy(ifr.ifr_name, devname);
  342. fd = socket(AF_INET, SOCK_DGRAM, 0);
  343. if (fd < 0) {
  344. dbg_time("Cannot get control socket: errno(%d)(%s)", errno, strerror(errno));
  345. return -1;
  346. }
  347. drvinfo.cmd = ETHTOOL_GDRVINFO;
  348. ifr.ifr_data = (void *)&drvinfo;
  349. if (ioctl(fd, SIOCETHTOOL, &ifr) < 0) {
  350. dbg_time("ioctl() error: errno(%d)(%s)", errno, strerror(errno));
  351. return -1;
  352. }
  353. dbg_time("netcard driver = %s, driver version = %s", drvinfo.driver, drvinfo.version);
  354. close(fd);
  355. return 0;
  356. }
  357. static void *catch_log(void *arg)
  358. {
  359. PROFILE_T *profile = (PROFILE_T *)arg;
  360. int nreads = 0;
  361. char tbuff[256+32];
  362. time_t t;
  363. struct tm *tm;
  364. char filter[10];
  365. size_t tsize = strlen("2020/06/22_22:50:07 ");
  366. sprintf(filter, ":%d:%03d:", profile->busnum, profile->devnum);
  367. while(1) {
  368. nreads = read(profile->usbmon_fd, tbuff + tsize, sizeof(tbuff) - tsize - 1);
  369. if (nreads <= 0) {
  370. break;
  371. }
  372. tbuff[tsize+nreads] = '\0'; // printf("%s", buff);
  373. if (!strstr(tbuff+tsize, filter))
  374. continue;
  375. time(&t);
  376. tm = localtime(&t);
  377. sprintf(tbuff, "%04d/%02d/%02d_%02d:%02d:%02d",
  378. tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec);
  379. tbuff[tsize-1] = ' ';
  380. write(profile->usbmon_logfile_fd, tbuff, strlen(tbuff));
  381. }
  382. return NULL;
  383. }
  384. int ql_capture_usbmon_log(PROFILE_T *profile, const char *log_path)
  385. {
  386. char usbmon_path[64];
  387. pthread_t pt;
  388. pthread_attr_t attr;
  389. if (access("/sys/module/usbmon", F_OK)) {
  390. dbg_time("usbmon is not load, please execute \"modprobe usbmon\" or \"insmod usbmon.ko\"");
  391. return -1;
  392. }
  393. if (access("/sys/kernel/debug/usb", F_OK)) {
  394. dbg_time("debugfs is not mount, please execute \"mount -t debugfs none_debugs /sys/kernel/debug\"");
  395. return -1;
  396. }
  397. sprintf(usbmon_path, "/sys/kernel/debug/usb/usbmon/%du", profile->busnum);
  398. profile->usbmon_fd = open(usbmon_path, O_RDONLY);
  399. if (profile->usbmon_fd < 0) {
  400. dbg_time("open %s error(%d) (%s)", usbmon_path, errno, strerror(errno));
  401. return -1;
  402. }
  403. profile->usbmon_logfile_fd = open(log_path, O_WRONLY | O_CREAT | O_APPEND, 0644);
  404. if (profile->usbmon_logfile_fd < 0) {
  405. dbg_time("open %s error(%d) (%s)", log_path, errno, strerror(errno));
  406. close(profile->usbmon_fd);
  407. profile->usbmon_fd = -1;
  408. return -1;
  409. }
  410. pthread_attr_init(&attr);
  411. pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
  412. pthread_create(&pt, &attr, catch_log, (void *)profile);
  413. return 0;
  414. }
  415. void ql_stop_usbmon_log(PROFILE_T *profile) {
  416. if (profile->usbmon_fd > 0)
  417. close(profile->usbmon_fd);
  418. if (profile->usbmon_logfile_fd > 0)
  419. close(profile->usbmon_logfile_fd);
  420. }