@@ -420,16 +420,27 @@ static void discover_cb(void *user_data)
static int gobi_enable(struct ofono_modem *modem)
{
struct gobi_data *data = ofono_modem_get_data(modem);
- const char *device;
+ const char *kernel_driver;
int r;
DBG("%p", modem);
- device = ofono_modem_get_string(modem, "Device");
- if (!device)
+ kernel_driver = ofono_modem_get_string(modem, "KernelDriver");
+ if (!kernel_driver)
return -EINVAL;
- data->device = qmi_device_new_qmux(device);
+ if (!strcmp(kernel_driver, "qrtr"))
+ data->device = qmi_device_new_qrtr();
+ else {
+ const char *device;
+
+ device = ofono_modem_get_string(modem, "Device");
+ if (!device)
+ return -EINVAL;
+
+ data->device = qmi_device_new_qmux(device);
+ }
+
if (!data->device)
return -EIO;
@@ -43,6 +43,7 @@ enum modem_type {
MODEM_TYPE_USB,
MODEM_TYPE_SERIAL,
MODEM_TYPE_PCIE,
+ MODEM_TYPE_EMBEDDED,
};
struct modem_info {
@@ -206,7 +207,10 @@ static int setup_qmi(struct modem_info *modem, const struct device_info *qmi,
DBG("qmi: %s net: %s kernel_driver: %s interface_number: %s",
qmi->devnode, net->devnode, net->kernel_driver, net->number);
- if (!qmi->kernel_driver || !net->number)
+ if (!qmi->kernel_driver)
+ return -EINVAL;
+
+ if (!net->number && modem->type != MODEM_TYPE_EMBEDDED)
return -EINVAL;
attr_value = udev_device_get_sysattr_value(net->udev_device,
@@ -234,6 +238,9 @@ static int setup_qmi(struct modem_info *modem, const struct device_info *qmi,
case MODEM_TYPE_PCIE:
ofono_modem_set_string(modem->modem, "Bus", "pcie");
break;
+ case MODEM_TYPE_EMBEDDED:
+ ofono_modem_set_string(modem->modem, "Bus", "embedded");
+ break;
case MODEM_TYPE_SERIAL:
break;
}
@@ -241,6 +248,75 @@ static int setup_qmi(struct modem_info *modem, const struct device_info *qmi,
return 0;
}
+static gboolean setup_gobi_qrtr_premux(struct modem_info *modem,
+ const char *name, int premux_index)
+{
+ const char *rmnet_data_prefix = "rmnet_data";
+ int rmnet_data_prefix_length = strlen(rmnet_data_prefix);
+ char buf[256];
+ int r;
+ uint32_t data_id;
+ uint32_t mux_id;
+
+ r = l_safe_atou32(name + rmnet_data_prefix_length, &data_id);
+ if (r < 0)
+ return FALSE;
+
+ mux_id = data_id + 1;
+
+ DBG("Adding premux interface %s, mux id: %d", name, mux_id);
+ sprintf(buf, "PremuxInterface%d", premux_index);
+ ofono_modem_set_string(modem->modem, buf, name);
+ sprintf(buf, "PremuxInterface%dMuxId", premux_index);
+ ofono_modem_set_integer(modem->modem, buf, mux_id);
+
+ return TRUE;
+}
+
+static gboolean setup_gobi_qrtr(struct modem_info *modem)
+{
+ const struct device_info *ipa_info = NULL;
+ int premux_count = 0;
+ int r;
+ GSList *list;
+
+ DBG("%s", modem->syspath);
+
+ for (list = modem->devices; list; list = list->next) {
+ struct device_info *info = list->data;
+ const char *name;
+
+ name = udev_device_get_sysname(info->udev_device);
+ if (l_str_has_prefix(name, "rmnet_ipa"))
+ ipa_info = info;
+ else if (l_str_has_prefix(name, "rmnet_data")) {
+ int premux_index = premux_count + 1;
+
+ if (setup_gobi_qrtr_premux(modem, name, premux_index))
+ premux_count++;
+ }
+ }
+
+ if (premux_count < 3) {
+ DBG("Not enough rmnet_data interfaces found");
+ return FALSE;
+ }
+
+ ofono_modem_set_integer(modem->modem, "NumPremuxInterfaces",
+ premux_count);
+
+ if (!ipa_info) {
+ DBG("No rmnet_ipa interface found");
+ return FALSE;
+ }
+
+ r = setup_qmi(modem, ipa_info, ipa_info);
+ if (r < 0)
+ return FALSE;
+
+ return TRUE;
+}
+
static gboolean setup_gobi(struct modem_info *modem)
{
const struct device_info *qmi = NULL;
@@ -1594,6 +1670,7 @@ static struct {
{ "wavecom", setup_wavecom },
{ "tc65", setup_tc65 },
{ "ehs6", setup_ehs6 },
+ { "gobiqrtr", setup_gobi_qrtr },
{ }
};
@@ -1644,6 +1721,7 @@ static void destroy_modem(gpointer data)
switch (modem->type) {
case MODEM_TYPE_USB:
case MODEM_TYPE_PCIE:
+ case MODEM_TYPE_EMBEDDED:
for (list = modem->devices; list; list = list->next) {
struct device_info *info = list->data;
@@ -1688,6 +1766,9 @@ static gboolean check_remove(gpointer key, gpointer value, gpointer user_data)
if (g_strcmp0(modem->serial->devpath, devpath) == 0)
return TRUE;
break;
+ case MODEM_TYPE_EMBEDDED:
+ /* Embedded modems cannot be removed. */
+ break;
}
return FALSE;
@@ -2133,6 +2214,26 @@ static void check_pci_device(struct udev_device *device)
device, kernel_driver);
}
+static void check_net_device(struct udev_device *device)
+{
+ char path[32];
+ const char *name;
+ const char *iflink;
+
+ name = udev_device_get_sysname(device);
+ if (!l_str_has_prefix(name, "rmnet_"))
+ return;
+
+ iflink = udev_device_get_sysattr_value(device, "iflink");
+ if (!iflink)
+ return;
+
+ /* Collect all rmnet devices with this iflink under a common path. */
+ sprintf(path, "/embedded/qrtr/%s", iflink);
+ add_device(path, NULL, "gobiqrtr", NULL, NULL, MODEM_TYPE_EMBEDDED,
+ device, "qrtr");
+}
+
static void check_device(struct udev_device *device)
{
const char *bus;
@@ -2149,6 +2250,8 @@ static void check_device(struct udev_device *device)
check_usb_device(device);
else if (g_str_equal(bus, "pci") == TRUE)
check_pci_device(device);
+ else if (g_str_equal(bus, "net") == TRUE)
+ check_net_device(device);
else
add_serial_device(device);