1 /******************************************************************************
2 @file qmap_bridge_mode.c
3 @brief Connectivity bridge manager.
4
5 DESCRIPTION
6 Connectivity Management Tool for USB network adapter of Quectel wireless cellular modules.
7
8 INITIALIZATION AND SEQUENCING REQUIREMENTS
9 None.
10
11 ---------------------------------------------------------------------------
12 Copyright (c) 2016 - 2020 Quectel Wireless Solution, Co., Ltd. All Rights Reserved.
13 Quectel Wireless Solution Proprietary and Confidential.
14 ---------------------------------------------------------------------------
15 ******************************************************************************/
16 #include "QMIThread.h"
17
ql_fread(const char * filename,void * buf,size_t size)18 static size_t ql_fread(const char *filename, void *buf, size_t size) {
19 FILE *fp = fopen(filename , "r");
20 size_t n = 0;
21
22 memset(buf, 0x00, size);
23
24 if (fp) {
25 n = fread(buf, 1, size, fp);
26 if (n <= 0 || n == size) {
27 dbg_time("warnning: fail to fread(%s), fread=%zu, buf_size=%zu: (%s)", filename, n, size, strerror(errno));
28 }
29 fclose(fp);
30 }
31
32 return n > 0 ? n : 0;
33 }
34
ql_fwrite(const char * filename,const void * buf,size_t size)35 static size_t ql_fwrite(const char *filename, const void *buf, size_t size) {
36 FILE *fp = fopen(filename , "w");
37 size_t n = 0;
38
39 if (fp) {
40 n = fwrite(buf, 1, size, fp);
41 if (n != size) {
42 dbg_time("warnning: fail to fwrite(%s), fwrite=%zu, buf_size=%zu: (%s)", filename, n, size, strerror(errno));
43 }
44 fclose(fp);
45 }
46
47 return n > 0 ? n : 0;
48 }
49
ql_bridge_mode_detect(PROFILE_T * profile)50 int ql_bridge_mode_detect(PROFILE_T *profile) {
51 const char *ifname = profile->qmapnet_adapter[0] ? profile->qmapnet_adapter : profile->usbnet_adapter;
52 const char *driver;
53 char bridge_mode[128];
54 char bridge_ipv4[128];
55 char ipv4[128];
56 char buf[64];
57 size_t n;
58 int in_bridge = 0;
59
60 driver = profile->driver_name;
61 snprintf(bridge_mode, sizeof(bridge_mode), "/sys/class/net/%s/bridge_mode", ifname);
62 snprintf(bridge_ipv4, sizeof(bridge_ipv4), "/sys/class/net/%s/bridge_ipv4", ifname);
63
64 if (access(bridge_ipv4, R_OK)) {
65 if (errno != ENOENT) {
66 dbg_time("fail to access %s, errno: %d (%s)", bridge_mode, errno, strerror(errno));
67 return 0;
68 }
69
70 snprintf(bridge_mode, sizeof(bridge_mode), "/sys/module/%s/parameters/bridge_mode", driver);
71 snprintf(bridge_ipv4, sizeof(bridge_ipv4), "/sys/module/%s/parameters/bridge_ipv4", driver);
72
73 if (access(bridge_mode, R_OK)) {
74 if (errno != ENOENT) {
75 dbg_time("fail to access %s, errno: %d (%s)", bridge_mode, errno, strerror(errno));
76 }
77 return 0;
78 }
79 }
80
81 n = ql_fread(bridge_mode, buf, sizeof(buf));
82 if (n > 0) {
83 in_bridge = (buf[0] != '0');
84 }
85 if (!in_bridge)
86 return 0;
87
88 memset(ipv4, 0, sizeof(ipv4));
89
90 if (strstr(bridge_ipv4, "/sys/class/net/") || profile->qmap_mode == 0 || profile->qmap_mode == 1) {
91 snprintf(ipv4, sizeof(ipv4), "0x%x", profile->ipv4.Address);
92 dbg_time("echo '%s' > %s", ipv4, bridge_ipv4);
93 ql_fwrite(bridge_ipv4, ipv4, strlen(ipv4));
94 }
95 else {
96 snprintf(ipv4, sizeof(ipv4), "0x%x:%d", profile->ipv4.Address, profile->muxid);
97 dbg_time("echo '%s' > %s", ipv4, bridge_ipv4);
98 ql_fwrite(bridge_ipv4, ipv4, strlen(ipv4));
99 }
100
101 return in_bridge;
102 }
103
ql_enable_qmi_wwan_rawip_mode(PROFILE_T * profile)104 int ql_enable_qmi_wwan_rawip_mode(PROFILE_T *profile) {
105 char filename[256];
106 char buf[4];
107 size_t n;
108 FILE *fp;
109
110 if (!qmidev_is_qmiwwan(profile->qmichannel))
111 return 0;
112
113 snprintf(filename, sizeof(filename), "/sys/class/net/%s/qmi/rawip", profile->usbnet_adapter);
114 n = ql_fread(filename, buf, sizeof(buf));
115
116 if (n == 0)
117 return 0;
118
119 if (buf[0] == '1' || buf[0] == 'Y')
120 return 0;
121
122 fp = fopen(filename , "w");
123 if (fp == NULL) {
124 dbg_time("Fail to fopen(%s, \"w\"), errno: %d (%s)", filename, errno, strerror(errno));
125 return 1;
126 }
127
128 buf[0] = 'Y';
129 n = fwrite(buf, 1, 1, fp);
130 if (n != 1) {
131 dbg_time("Fail to fwrite(%s), errno: %d (%s)", filename, errno, strerror(errno));
132 fclose(fp);
133 return 1;
134 }
135 fclose(fp);
136
137 return 0;
138 }
139
ql_driver_type_detect(PROFILE_T * profile)140 int ql_driver_type_detect(PROFILE_T *profile) {
141 if (qmidev_is_gobinet(profile->qmichannel)) {
142 profile->qmi_ops = &gobi_qmidev_ops;
143 }
144 else {
145 profile->qmi_ops = &qmiwwan_qmidev_ops;
146 }
147 qmidev_send = profile->qmi_ops->send;
148
149 return 0;
150 }
151
ql_set_driver_bridge_mode(PROFILE_T * profile)152 void ql_set_driver_bridge_mode(PROFILE_T *profile) {
153 char enable[16];
154 char filename[256];
155
156 if(profile->qmap_mode)
157 snprintf(filename, sizeof(filename), "/sys/class/net/%s/bridge_mode", profile->qmapnet_adapter);
158 else
159 snprintf(filename, sizeof(filename), "/sys/class/net/%s/bridge_mode", profile->usbnet_adapter);
160 snprintf(enable, sizeof(enable), "%02d\n", profile->enable_bridge);
161 ql_fwrite(filename, enable, sizeof(enable));
162 }
163
ql_qmi_qmap_mode_detect(PROFILE_T * profile)164 static int ql_qmi_qmap_mode_detect(PROFILE_T *profile) {
165 char buf[128];
166 int n;
167 struct {
168 char filename[255 * 2];
169 char linkname[255 * 2];
170 } *pl;
171
172 pl = (typeof(pl)) malloc(sizeof(*pl));
173
174 snprintf(pl->linkname, sizeof(pl->linkname), "/sys/class/net/%s/device/driver", profile->usbnet_adapter);
175 n = readlink(pl->linkname, pl->filename, sizeof(pl->filename));
176 pl->filename[n] = '\0';
177 while (pl->filename[n] != '/')
178 n--;
179 strncpy(profile->driver_name, &pl->filename[n+1], sizeof(profile->driver_name));
180
181 ql_get_driver_rmnet_info(profile, &profile->rmnet_info);
182 if (profile->rmnet_info.size) {
183 profile->qmap_mode = profile->rmnet_info.qmap_mode;
184 if (profile->qmap_mode) {
185 int offset_id = (profile->muxid == 0)? profile->pdp - 1 : profile->muxid - 0x81;
186
187 if (profile->qmap_mode == 1)
188 offset_id = 0;
189 profile->muxid = profile->rmnet_info.mux_id[offset_id];
190 strncpy(profile->qmapnet_adapter, profile->rmnet_info.ifname[offset_id], sizeof(profile->qmapnet_adapter));
191 profile->qmap_size = profile->rmnet_info.rx_urb_size;
192 profile->qmap_version = profile->rmnet_info.qmap_version;
193 }
194
195 goto _out;
196 }
197
198 snprintf(pl->filename, sizeof(pl->filename), "/sys/class/net/%s/qmap_mode", profile->usbnet_adapter);
199 if (access(pl->filename, R_OK)) {
200 if (errno != ENOENT) {
201 dbg_time("fail to access %s, errno: %d (%s)", pl->filename, errno, strerror(errno));
202 goto _out;
203 }
204
205 snprintf(pl->filename, sizeof(pl->filename), "/sys/module/%s/parameters/qmap_mode", profile->driver_name);
206 if (access(pl->filename, R_OK)) {
207 if (errno != ENOENT) {
208 dbg_time("fail to access %s, errno: %d (%s)", pl->filename, errno, strerror(errno));
209 goto _out;
210 }
211
212 snprintf(pl->filename, sizeof(pl->filename), "/sys/class/net/%s/device/driver/module/parameters/qmap_mode", profile->usbnet_adapter);
213 if (access(pl->filename, R_OK)) {
214 if (errno != ENOENT) {
215 dbg_time("fail to access %s, errno: %d (%s)", pl->filename, errno, strerror(errno));
216 goto _out;
217 }
218 }
219 }
220 }
221
222 if (!access(pl->filename, R_OK)) {
223 n = ql_fread(pl->filename, buf, sizeof(buf));
224 if (n > 0) {
225 profile->qmap_mode = atoi(buf);
226
227 if (profile->qmap_mode > 1) {
228 if(!profile->muxid)
229 profile->muxid = profile->pdp + 0x80; //muxis is 0x8X for PDN-X
230 snprintf(profile->qmapnet_adapter, sizeof(profile->qmapnet_adapter),
231 "%.16s.%d", profile->usbnet_adapter, profile->muxid - 0x80);
232 } if (profile->qmap_mode == 1) {
233 profile->muxid = 0x81;
234 strncpy(profile->qmapnet_adapter, profile->usbnet_adapter, sizeof(profile->qmapnet_adapter));
235 }
236 }
237 }
238 else if (qmidev_is_qmiwwan(profile->qmichannel)) {
239 snprintf(pl->filename, sizeof(pl->filename), "/sys/class/net/qmimux%d", profile->pdp - 1);
240 if (access(pl->filename, R_OK)) {
241 if (errno != ENOENT) {
242 dbg_time("fail to access %s, errno: %d (%s)", pl->filename, errno, strerror(errno));
243 }
244 goto _out;
245 }
246
247 //upstream Kernel Style QMAP qmi_wwan.c
248 snprintf(pl->filename, sizeof(pl->filename), "/sys/class/net/%s/qmi/add_mux", profile->usbnet_adapter);
249 n = ql_fread(pl->filename, buf, sizeof(buf));
250 if (n >= 5) {
251 dbg_time("If use QMAP by /sys/class/net/%s/qmi/add_mux", profile->usbnet_adapter);
252 dbg_time("File:%s Line:%d Please make sure add next patch to qmi_wwan.c", __func__, __LINE__);
253 /*
254 diff --git a/drivers/net/usb/qmi_wwan.c b/drivers/net/usb/qmi_wwan.c
255 index 74bebbd..db8a777 100644
256 --- a/drivers/net/usb/qmi_wwan.c
257 +++ b/drivers/net/usb/qmi_wwan.c
258 @@ -379,6 +379,24 @@ static ssize_t add_mux_store(struct device *d, struct device_attribute *attr, c
259 if (!ret) {
260 info->flags |= QMI_WWAN_FLAG_MUX;
261 ret = len;
262 +#if 1 //Add by Quectel
263 + if (le16_to_cpu(dev->udev->descriptor.idVendor) == 0x2c7c) {
264 + int idProduct = le16_to_cpu(dev->udev->descriptor.idProduct);
265 +
266 + if (idProduct == 0x0121 || idProduct == 0x0125 || idProduct == 0x0435) //MDM9x07
267 + dev->rx_urb_size = 4*1024;
268 + else if (idProduct == 0x0306) //MDM9x40
269 + dev->rx_urb_size = 16*1024;
270 + else if (idProduct == 0x0512) //SDX20
271 + dev->rx_urb_size = 32*1024;
272 + else if (idProduct == 0x0620) //SDX24
273 + dev->rx_urb_size = 32*1024;
274 + else if (idProduct == 0x0800) //SDX55
275 + dev->rx_urb_size = 32*1024;
276 + else
277 + dev->rx_urb_size = 32*1024;
278 + }
279 +#endif
280 }
281 err:
282 rtnl_unlock();
283 */
284 profile->qmap_mode = n/5; //0x11\n0x22\n0x33\n
285 if (profile->qmap_mode > 1) {
286 //PDN-X map to qmimux-X
287 if(!profile->muxid) {
288 profile->muxid = (buf[5*(profile->pdp - 1) + 2] - '0')*16 + (buf[5*(profile->pdp - 1) + 3] - '0');
289 snprintf(profile->qmapnet_adapter, sizeof(profile->qmapnet_adapter), "qmimux%d", profile->pdp - 1);
290 } else {
291 profile->muxid = (buf[5*(profile->muxid - 0x81) + 2] - '0')*16 + (buf[5*(profile->muxid - 0x81) + 3] - '0');
292 snprintf(profile->qmapnet_adapter, sizeof(profile->qmapnet_adapter), "qmimux%d", profile->muxid - 0x81);
293 }
294 } else if (profile->qmap_mode == 1) {
295 profile->muxid = (buf[5*0 + 2] - '0')*16 + (buf[5*0 + 3] - '0');
296 snprintf(profile->qmapnet_adapter, sizeof(profile->qmapnet_adapter),
297 "qmimux%d", 0);
298 }
299 }
300 }
301
302 _out:
303 if (profile->qmap_mode) {
304 if (profile->qmap_size == 0) {
305 profile->qmap_size = 16*1024;
306 snprintf(pl->filename, sizeof(pl->filename), "/sys/class/net/%s/qmap_size", profile->usbnet_adapter);
307 if (!access(pl->filename, R_OK)) {
308 size_t n;
309 char buf[32];
310 n = ql_fread(pl->filename, buf, sizeof(buf));
311 if (n > 0) {
312 profile->qmap_size = atoi(buf);
313 }
314 }
315 }
316
317 if (profile->qmap_version == 0) {
318 profile->qmap_version = WDA_DL_DATA_AGG_QMAP_ENABLED;
319 }
320
321 dbg_time("qmap_mode = %d, qmap_version = %d, qmap_size = %d, muxid = 0x%02x, qmap_netcard = %s",
322 profile->qmap_mode, profile->qmap_version, profile->qmap_size, profile->muxid, profile->qmapnet_adapter);
323 }
324 ql_set_driver_bridge_mode(profile);
325 free(pl);
326
327 return 0;
328 }
329
ql_mbim_usb_vlan_mode_detect(PROFILE_T * profile)330 static int ql_mbim_usb_vlan_mode_detect(PROFILE_T *profile) {
331 char tmp[128];
332
333 snprintf(tmp, sizeof(tmp), "/sys/class/net/%s.%d", profile->usbnet_adapter, profile->pdp);
334 if (!access(tmp, F_OK)) {
335 profile->qmap_mode = 4;
336 profile->muxid = profile->pdp;
337 strncpy(profile->qmapnet_adapter, &tmp[strlen("/sys/class/net/")], sizeof(profile->qmapnet_adapter));
338
339 dbg_time("mbim_qmap_mode = %d, vlan_id = 0x%02x, qmap_netcard = %s",
340 profile->qmap_mode, profile->muxid, profile->qmapnet_adapter);
341 }
342
343 return 0;
344 }
345
ql_mbim_mhi_qmap_mode_detect(PROFILE_T * profile)346 static int ql_mbim_mhi_qmap_mode_detect(PROFILE_T *profile) {
347 ql_get_driver_rmnet_info(profile, &profile->rmnet_info);
348 if (profile->rmnet_info.size) {
349 profile->qmap_mode = profile->rmnet_info.qmap_mode;
350 if (profile->qmap_mode) {
351 int offset_id = profile->pdp - 1;
352
353 if (profile->qmap_mode == 1)
354 offset_id = 0;
355 profile->muxid = offset_id;
356 strcpy(profile->qmapnet_adapter, profile->rmnet_info.ifname[offset_id]);
357 profile->qmap_size = profile->rmnet_info.rx_urb_size;
358 profile->qmap_version = profile->rmnet_info.qmap_version;
359
360 dbg_time("mbim_qmap_mode = %d, vlan_id = 0x%02x, qmap_netcard = %s",
361 profile->qmap_mode, profile->muxid, profile->qmapnet_adapter);
362 }
363
364 goto _out;
365 }
366
367 _out:
368 return 0;
369 }
370
ql_qmap_mode_detect(PROFILE_T * profile)371 int ql_qmap_mode_detect(PROFILE_T *profile) {
372 if (profile->software_interface == SOFTWARE_MBIM) {
373 if (profile->hardware_interface == HARDWARE_USB)
374 return ql_mbim_usb_vlan_mode_detect(profile);
375 else if (profile->hardware_interface == HARDWARE_PCIE)
376 return ql_mbim_mhi_qmap_mode_detect(profile);
377 } else if (profile->software_interface == SOFTWARE_QMI) {
378 return ql_qmi_qmap_mode_detect(profile);
379 }
380 return 0;
381 }
382