Add a way to get and set data format expected by kernel device driver. This is inspired by what is done in qmicli (package libqmi). It does not use QMI protocol but a sysfs exported by kernel driver. To use this feature, kernel version must be equal or more than 4.5. --- drivers/qmimodem/qmi.c | 200 +++++++++++++++++++++++++++++++++++++++++++++++++ drivers/qmimodem/qmi.h | 10 +++ 2 files changed, 210 insertions(+)
diff --git a/drivers/qmimodem/qmi.c b/drivers/qmimodem/qmi.c index 0080f250..85b40693 100644 --- a/drivers/qmimodem/qmi.c +++ b/drivers/qmimodem/qmi.c @@ -26,6 +26,8 @@ #define _GNU_SOURCE #include <stdio.h> #include <ctype.h> +#include <dirent.h> +#include <errno.h> #include <fcntl.h> #include <unistd.h> #include <stdlib.h> @@ -33,6 +35,8 @@ #include <glib.h> +#include <ofono/log.h> + #include "qmi.h" #include "ctl.h" @@ -1234,6 +1238,202 @@ bool qmi_device_shutdown(struct qmi_device *device, qmi_shutdown_func_t func, return true; } +static bool get_device_file_name(struct qmi_device *device, + char *file_name, int size) +{ + pid_t pid; + char temp[100]; + ssize_t result; + + if (size <= 0) + return false; + + pid = getpid(); + + snprintf(temp, 100, "/proc/%d/fd/%d", (int) pid, device->fd); + temp[99] = 0; + + result = readlink(temp, file_name, size - 1); + + if (result == -1 || result >= size - 1) { + DBG("Error %d in readlink", errno); + return false; + } + + file_name[result] = 0; + + return true; +} + +static char *get_first_dir_in_directory(char *dir_path) +{ + DIR *dir; + struct dirent *dir_entry; + char *dir_name = NULL; + + dir = opendir(dir_path); + + if (!dir) + return NULL; + + dir_entry = readdir(dir); + + while ((dir_entry != NULL)) { + if (dir_entry->d_type == DT_DIR && + strcmp(dir_entry->d_name, ".") != 0 && + strcmp(dir_entry->d_name, "..") != 0) { + dir_name = g_strdup(dir_entry->d_name); + break; + } + + dir_entry = readdir(dir); + } + + closedir(dir); + return dir_name; +} + +static char *get_device_interface(struct qmi_device *device) +{ + char * const driver_names[] = { "usbmisc", "usb" }; + unsigned int i; + char file_path[PATH_MAX]; + char *file_name; + char *interface = NULL; + + if (!get_device_file_name(device, file_path, sizeof(file_path))) + return NULL; + + file_name = basename(file_path); + + for (i = 0; i < G_N_ELEMENTS(driver_names) && !interface; i++) { + gchar *sysfs_path; + + sysfs_path = g_strdup_printf("/sys/class/%s/%s/device/net/", + driver_names[i], file_name); + interface = get_first_dir_in_directory(sysfs_path); + g_free(sysfs_path); + } + + return interface; +} + +enum qmi_device_expected_data_format qmi_device_get_expected_data_format( + struct qmi_device *device) +{ + char *sysfs_path = NULL; + char *interface = NULL; + int fd = -1; + char value; + enum qmi_device_expected_data_format expected = + QMI_DEVICE_EXPECTED_DATA_FORMAT_UNKNOWN; + + if (!device) + goto done; + + interface = get_device_interface(device); + + if (!interface) { + DBG("Error while getting interface name"); + goto done; + } + + /* Build sysfs file path and open it */ + sysfs_path = g_strdup_printf("/sys/class/net/%s/qmi/raw_ip", interface); + + fd = open(sysfs_path, O_RDONLY); + if (fd < 0) { + /* maybe not supported by kernel */ + DBG("Error %d in open(%s)", errno, sysfs_path); + goto done; + } + + if (read(fd, &value, 1) != 1) { + DBG("Error %d in read(%s)", errno, sysfs_path); + goto done; + } + + if (value == 'Y') + expected = QMI_DEVICE_EXPECTED_DATA_FORMAT_RAW_IP; + else if (value == 'N') + expected = QMI_DEVICE_EXPECTED_DATA_FORMAT_802_3; + else + DBG("Unexpected sysfs file contents"); + +done: + if (fd >= 0) + close(fd); + + if (sysfs_path) + g_free(sysfs_path); + + if (interface) + g_free(interface); + + return expected; +} + +bool qmi_device_set_expected_data_format(struct qmi_device *device, + enum qmi_device_expected_data_format format) +{ + bool res = false; + char *sysfs_path = NULL; + char *interface = NULL; + int fd = -1; + char value; + + if (!device) + goto done; + + switch (format) { + case QMI_DEVICE_EXPECTED_DATA_FORMAT_802_3: + value = 'N'; + break; + case QMI_DEVICE_EXPECTED_DATA_FORMAT_RAW_IP: + value = 'Y'; + break; + default: + DBG("Unhandled firmat: %d", (int) format); + goto done; + } + + interface = get_device_interface(device); + + if (!interface) { + DBG("Error while getting interface name"); + goto done; + } + + /* Build sysfs file path and open it */ + sysfs_path = g_strdup_printf("/sys/class/net/%s/qmi/raw_ip", interface); + + fd = open(sysfs_path, O_WRONLY); + if (fd < 0) { + /* maybe not supported by kernel */ + DBG("Error %d in open(%s)", errno, sysfs_path); + goto done; + } + + if (write(fd, &value, 1) != 1) { + DBG("Error %d in write(%s)", errno, sysfs_path); + goto done; + } + + res = true; + +done: + if (fd >= 0) + close(fd); + + if (sysfs_path) + g_free(sysfs_path); + + if (interface) + g_free(interface); + + return res; +} + struct qmi_param *qmi_param_new(void) { struct qmi_param *param; diff --git a/drivers/qmimodem/qmi.h b/drivers/qmimodem/qmi.h index dca115c4..ac19fe01 100644 --- a/drivers/qmimodem/qmi.h +++ b/drivers/qmimodem/qmi.h @@ -47,6 +47,12 @@ #define QMI_SERVICE_RMS 225 /* Remote management service */ #define QMI_SERVICE_OMA 226 /* OMA device management service */ +enum qmi_device_expected_data_format { + QMI_DEVICE_EXPECTED_DATA_FORMAT_UNKNOWN, + QMI_DEVICE_EXPECTED_DATA_FORMAT_802_3, + QMI_DEVICE_EXPECTED_DATA_FORMAT_RAW_IP, +}; + struct qmi_version { uint8_t type; uint16_t major; @@ -82,6 +88,10 @@ bool qmi_device_discover(struct qmi_device *device, qmi_discover_func_t func, bool qmi_device_shutdown(struct qmi_device *device, qmi_shutdown_func_t func, void *user_data, qmi_destroy_func_t destroy); +enum qmi_device_expected_data_format qmi_device_get_expected_data_format( + struct qmi_device *device); +bool qmi_device_set_expected_data_format(struct qmi_device *device, + enum qmi_device_expected_data_format format); struct qmi_param; -- 2.11.0 _______________________________________________ ofono mailing list ofono@ofono.org https://lists.ofono.org/mailman/listinfo/ofono