• source navigation  • diff markup  • identifier search  • freetext search  • 

Sources/uqmi/uqmid/wwan.c

  1 /*
  2  * uqmid -- implement a daemon based on the uqmi idea
  3  *
  4  * Copyright (C) 2023-2024 Alexander Couzens <lynxis@fe80.eu>
  5  *
  6  * This library is free software; you can redistribute it and/or
  7  * modify it under the terms of the GNU Lesser General Public
  8  * License as published by the Free Software Foundation; either
  9  * version 2 of the License, or (at your option) any later version.
 10  *
 11  * This library is distributed in the hope that it will be useful,
 12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
 13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 14  * Lesser General Public License for more details.
 15  *
 16  * You should have received a copy of the GNU Lesser General Public
 17  * License along with this library; if not, write to the
 18  * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 19  * Boston, MA 02110-1301 USA.
 20  */
 21 
 22 /** Interface with the linux kernel module */
 23 
 24 #include <errno.h>
 25 #include <fcntl.h>
 26 #include <unistd.h>
 27 #include <dirent.h>
 28 #include <libgen.h>
 29 #include <linux/limits.h>
 30 #include <talloc.h>
 31 
 32 #include <sys/ioctl.h>
 33 #include <linux/if_ether.h>
 34 #include <linux/if.h>
 35 #include <linux/sockios.h>
 36 
 37 #include "wwan.h"
 38 #include "logging.h"
 39 #include "modem.h"
 40 
 41 static int get_real_device(const char *cdc_device_path, char *real_path, size_t real_path_size)
 42 {
 43         ssize_t ret = readlink(cdc_device_path, real_path, real_path_size - 1);
 44         if (ret == -1) {
 45                 strncpy(real_path, cdc_device_path, real_path_size - 1);
 46         } else if (ret == real_path_size - 1) {
 47                 real_path[real_path_size - 1] = 0;
 48         }
 49 
 50         return 0;
 51 }
 52 
 53 static int get_real_device_name(const char *cdc_device_path, char *real_name, size_t real_name_size)
 54 {
 55         char real_device[PATH_MAX];
 56         char *base;
 57 
 58         get_real_device(cdc_device_path, real_device, PATH_MAX);
 59         base = basename(real_device);
 60         strncpy(real_name, base, real_name_size - 1);
 61 
 62         return 0;
 63 }
 64 
 65 static int _get_wwan_device(const char *sysfs_path,
 66                             char *wwan_device, size_t wwan_device_size)
 67 {
 68         DIR *dirfd;
 69         int found = 0;
 70         struct dirent *e;
 71 
 72         dirfd = opendir(sysfs_path);
 73         if (!dirfd) {
 74                 return -1;
 75         }
 76 
 77         while ((e = readdir(dirfd)) != NULL)
 78         {
 79                 if (!strncmp(e->d_name, ".", strlen(e->d_name)) ||
 80                     !strncmp(e->d_name, "..", strlen(e->d_name)))
 81                         continue;
 82 
 83                 if (found == 0)
 84                         strncpy(wwan_device, e->d_name, wwan_device_size - 1);
 85                 found++;
 86         }
 87 
 88         closedir(dirfd);
 89         return found;
 90 }
 91 
 92 /**
 93  *
 94  * @param modem
 95  * @param wwan_device result if found
 96  * @param wwan_device_size
 97  * @return 0 on success
 98  */
 99 int wwan_refresh_device(struct modem *modem)
100 {
101         char sysfs_path[PATH_MAX];
102         char cdcname[128];
103         char wwan_device[17];
104         int ret;
105 
106         get_real_device_name(modem->device, cdcname, sizeof(cdcname));
107         TALLOC_FREE(modem->wwan.dev);
108 
109         /*
110          * usbmisc >= 3.6
111          * usb < 3.6
112          */
113         snprintf(&sysfs_path[0], sizeof(sysfs_path)-1, "/sys/class/%s/%s/device/net/", "usbmisc", cdcname);
114         ret = _get_wwan_device(sysfs_path, wwan_device, sizeof(wwan_device));
115         if (ret >= 1) {
116                 snprintf(sysfs_path, sizeof(sysfs_path)-1, "/sys/class/%s/%s/device/net/%s", "usbmisc", cdcname, wwan_device);
117                 modem->wwan.dev = talloc_strdup(modem, wwan_device);
118                 modem->wwan.sysfs = talloc_strdup(modem, sysfs_path);
119                 modem->subsystem_name = "usbmisc";
120                 return 0;
121         }
122 
123         snprintf(sysfs_path, sizeof(sysfs_path)-1, "/sys/class/%s/%s/device/net/", "usb", wwan_device);
124         ret = _get_wwan_device(sysfs_path, wwan_device, sizeof(wwan_device));
125         if (ret >= 1) {
126                 snprintf(sysfs_path, sizeof(sysfs_path)-1, "/sys/class/%s/%s/device/net/%s", "usbmisc", cdcname, wwan_device);
127                 modem->wwan.dev = talloc_strdup(modem, wwan_device);
128                 modem->wwan.sysfs = talloc_strdup(modem, sysfs_path);
129                 modem->subsystem_name = "usb";
130                 return 0;
131         }
132 
133         return -1;
134 }
135 
136 /* read_uint_from_file from fstools under GPLv2 */
137 static int
138 read_char_from_file(const char *dirname, const char *filename, char *achar)
139 {
140         FILE *f;
141         char fname[PATH_MAX];
142         int ret = -1;
143 
144         snprintf(fname, sizeof(fname), "%s/%s", dirname, filename);
145         f = fopen(fname, "r");
146         if (!f)
147                 return ret;
148 
149         if (fread(achar, 1, 1, f) == 1)
150                 ret = 0;
151 
152         fclose(f);
153         return ret;
154 }
155 
156 static int
157 write_char_to_file(const char *dirname, const char *filename, const char *achar)
158 {
159         FILE *f;
160         char fname[PATH_MAX];
161         int ret = -1;
162 
163         snprintf(fname, sizeof(fname), "%s/%s", dirname, filename);
164 
165         f = fopen(fname, "w");
166         if (!f)
167                 return ret;
168 
169         if (fwrite(achar, 1, 1, f) == 1)
170                 ret = 0;
171 
172         fclose(f);
173         return ret;
174 }
175 
176 /* use ioctl until uqmid has netlink support */
177 int wwan_set_mtu(const char *netif, unsigned int mtu)
178 {
179         int sock, rc;
180         struct ifreq req;
181 
182         sock = socket(AF_INET, SOCK_DGRAM, 0);
183         if (sock < 0)
184                 return sock;
185 
186         memset(&req, 0, sizeof req);
187         strncpy(req.ifr_name, netif, sizeof(req.ifr_name));
188 
189         rc = ioctl(sock, SIOCGIFMTU, &req);
190         if (rc < 0) {
191                 close(sock);
192                 return rc;
193         }
194 
195         if (req.ifr_mtu == mtu) {
196                 close(sock);
197                 return 0;
198         }
199 
200         req.ifr_mtu = mtu;
201 
202         rc = ioctl(sock, SIOCSIFMTU, &req);
203         close(sock);
204         return rc;
205 }
206 
207 /* use ioctl until uqmid has netlink support */
208 int wwan_ifupdown(const char *netif, bool up)
209 {
210         int sock, rc;
211         struct ifreq req;
212 
213         sock = socket(AF_INET, SOCK_DGRAM, 0);
214         if (sock < 0)
215                 return sock;
216 
217         memset(&req, 0, sizeof req);
218         strncpy(req.ifr_name, netif, sizeof(req.ifr_name));
219 
220         rc = ioctl(sock, SIOCGIFFLAGS, &req);
221         if (rc < 0) {
222                 close(sock);
223                 return rc;
224         }
225 
226         if ((req.ifr_flags & IFF_UP) == up) {
227                 close(sock);
228                 return 0;
229         }
230 
231         if (up)
232                 req.ifr_flags |= IFF_UP;
233         else
234                 req.ifr_flags &= ~IFF_UP;
235 
236         rc = ioctl(sock, SIOCSIFFLAGS, &req);
237         close(sock);
238         return rc;
239 }
240 
241 int wwan_read_configuration(const char *sysfs_path, struct wwan_conf *config)
242 {
243         char tmp = 0;
244         int ret;
245         char qmi_path[PATH_MAX];
246 
247         snprintf(qmi_path, sizeof(qmi_path) - 1, "%s/qmi", sysfs_path);
248         ret = read_char_from_file(qmi_path, "raw_ip", &tmp);
249         if (ret)
250                 return -ENOENT;
251         config->raw_ip = (tmp == 'Y');
252 
253         ret = read_char_from_file(qmi_path, "pass_through", &tmp);
254         if (ret)
255                 return -ENOENT;
256 
257         config->pass_through = (tmp == 'Y');
258 
259         return 0;
260 }
261 
262 /**
263  *
264  * @param sysfs_path path to the network sysfs path
265  * @param config
266  * @return 0 on success
267  */
268 int
269 wwan_set_configuration(const char *sysfs_path, const struct wwan_conf *config)
270 {
271         struct wwan_conf old = { 0 };
272         int ret;
273         char yes = 'Y';
274         char no = 'N';
275         char qmi_path[PATH_MAX];
276 
277         snprintf(qmi_path, sizeof(qmi_path) - 1, "%s/qmi", sysfs_path);
278         ret = wwan_read_configuration(sysfs_path, &old);
279         if (ret) {
280                 return -ENOENT;
281         }
282 
283         if (config->raw_ip != old.raw_ip) {
284                 ret = write_char_to_file(qmi_path, "raw_ip", config->raw_ip ? &yes : &no);
285                 if (ret) {
286                         return -EINVAL;
287                 }
288         }
289 
290         if (config->pass_through != old.pass_through) {
291                 ret = write_char_to_file(qmi_path, "pass_through", config->pass_through ? &yes : &no);
292                 if (ret) {
293                         return -EINVAL;
294                 }
295         }
296 
297         return 0;
298 }
299 

This page was automatically generated by LXR 0.3.1.  •  OpenWrt