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