diff --git a/Makefile.am b/Makefile.am
index 85bebae9..afec6997 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -386,6 +386,11 @@ builtin_sources += $(qmi_sources) \
 			drivers/qmimodem/call-forwarding.c
 
 builtin_sources += plugins/gobi.c
+
+if QMIMODEM_QRTR
+builtin_modules += gobi
+endif
+
 endif
 
 if MBIMMODEM
diff --git a/configure.ac b/configure.ac
index 9fa6531a..14b37eef 100644
--- a/configure.ac
+++ b/configure.ac
@@ -227,6 +227,15 @@ AC_ARG_ENABLE(qmimodem, AS_HELP_STRING([--disable-qmimodem],
 					[enable_qmimodem=${enableval}])
 AM_CONDITIONAL(QMIMODEM, test "${enable_qmimodem}" != "no")
 
+AC_ARG_ENABLE(qrtr, AS_HELP_STRING([--enable_qrtr],
+				[enable Qualcomm QRTR modem support (WIP)]),
+					[enable_qrtr=${enableval}],
+					[enable_qrtr=no])
+AM_CONDITIONAL(QMIMODEM_QRTR, test "${enable_qrtr}" != "no")
+if (test "${enable_qrtr}" == "yes"); then
+	AC_DEFINE(QMIMODEM_QRTR, [], [forces Qualcomm QRTR instead of QMUX])
+fi
+
 AC_ARG_ENABLE(mbimmodem, AS_HELP_STRING([--disable-mbimmodem],
 				[disable MBIM modem support]),
 					[enable_mbimmodem=${enableval}])
diff --git a/drivers/qmimodem/qmi.c b/drivers/qmimodem/qmi.c
index 35751d7c..48f9ce10 100644
--- a/drivers/qmimodem/qmi.c
+++ b/drivers/qmimodem/qmi.c
@@ -33,6 +33,8 @@
 #include <stdlib.h>
 #include <string.h>
 #include <linux/limits.h>
+#include <linux/qrtr.h>
+#include <sys/socket.h>
 
 #include <ell/ell.h>
 
@@ -1874,6 +1876,235 @@ struct qmi_device *qmi_device_new_qmux(const char *device)
 	return &qmux->super;
 }
 
+struct qmi_device_qrtr {
+	struct qmi_device super;
+	struct discover_data *discover_data;
+};
+
+static void __debug_ctrl_request(const struct qrtr_ctrl_pkt *packet,
+					qmi_debug_func_t function,
+					void *user_data)
+{
+	char strbuf[72 + 16], *str;
+	const char *type;
+
+	if (!function)
+		return;
+
+	str = strbuf;
+	str += sprintf(str, "    %s",
+			__service_type_to_string(QMI_SERVICE_CONTROL));
+
+	type = "_pkt";
+
+	str += sprintf(str, "%s cmd=%d", type,
+				L_LE32_TO_CPU(packet->cmd));
+
+	function(strbuf, user_data);
+}
+
+static void handle_control_packet(struct qmi_device_qrtr *qrtr,
+					const struct qrtr_ctrl_pkt *packet)
+{
+	struct qmi_device *device = &qrtr->super;
+	uint32_t cmd;
+	uint32_t type;
+	uint32_t instance;
+	uint32_t version;
+	uint32_t node;
+	uint32_t port;
+
+	__debug_ctrl_request(packet, device->debug_func,
+				device->debug_data);
+
+	cmd = L_LE32_TO_CPU(packet->cmd);
+	if (cmd != QRTR_TYPE_NEW_SERVER) {
+		DBG("Unknown command: %d", cmd);
+		return;
+	}
+
+	if (!packet->server.service && !packet->server.instance &&
+		!packet->server.node && !packet->server.port) {
+		DBG("Initial service discovery has completed");
+
+		if (qrtr->discover_data->func)
+			qrtr->discover_data->func(qrtr->discover_data->user_data);
+
+		discover_data_free(qrtr->discover_data);
+		qrtr->discover_data = NULL;
+
+		return;
+	}
+
+	type = L_LE32_TO_CPU(packet->server.service);
+	version = L_LE32_TO_CPU(packet->server.instance) & 0xff;
+	instance = L_LE32_TO_CPU(packet->server.instance) >> 8;
+
+	node = L_LE32_TO_CPU(packet->server.node);
+	port = L_LE32_TO_CPU(packet->server.port);
+
+	if (cmd == QRTR_TYPE_NEW_SERVER) {
+		DBG("New server: Type: %d Version: %d Instance: %d Node: %d Port: %d",
+			type, version, instance, node, port);
+	}
+}
+
+static void handle_packet(struct qmi_device_qrtr *qrtr, uint32_t sending_port,
+				const void *buf, ssize_t len)
+{
+	const struct qrtr_ctrl_pkt *packet = buf;
+
+	if (sending_port != QRTR_PORT_CTRL) {
+		DBG("Receive of service data is not implemented");
+		return;
+	}
+
+	if ((unsigned long) len < sizeof(*packet)) {
+		DBG("qrtr control packet is too small");
+		return;
+	}
+
+	handle_control_packet(qrtr, packet);
+}
+
+static bool received_qrtr_data(struct l_io *io, void *user_data)
+{
+	struct qmi_device_qrtr *qrtr = user_data;
+	struct sockaddr_qrtr addr;
+	unsigned char buf[2048];
+	ssize_t bytes_read;
+	socklen_t addr_size;
+
+	addr_size = sizeof(addr);
+	bytes_read = recvfrom(l_io_get_fd(qrtr->super.io), buf, sizeof(buf), 0,
+				(struct sockaddr *) &addr, &addr_size);
+	DBG("Received %ld bytes from Node: %d Port: 0x%x", bytes_read,
+		addr.sq_node, addr.sq_port);
+
+	if (bytes_read < 0)
+		return true;
+
+	l_util_hexdump(true, buf, bytes_read, qrtr->super.debug_func,
+			qrtr->super.debug_data);
+
+	handle_packet(qrtr, addr.sq_port, buf, bytes_read);
+
+	return true;
+}
+
+static int qmi_device_qrtr_discover(struct qmi_device *device,
+					qmi_discover_func_t func,
+					void *user_data,
+					qmi_destroy_func_t destroy)
+{
+	struct qmi_device_qrtr *qrtr =
+		l_container_of(device, struct qmi_device_qrtr, super);
+	struct discover_data *data;
+	struct qrtr_ctrl_pkt packet;
+	struct sockaddr_qrtr addr;
+	socklen_t addr_len;
+	int rc;
+	ssize_t bytes_written;
+	int fd;
+
+	__debug_device(device, "device %p discover", device);
+
+	if (qrtr->discover_data)
+		return -EINPROGRESS;
+
+	data = l_new(struct discover_data, 1);
+
+	data->super.destroy = discover_data_free;
+	data->device = device;
+	data->func = func;
+	data->user_data = user_data;
+	data->destroy = destroy;
+
+	fd = l_io_get_fd(device->io);
+
+	/*
+	 * The control node is configured by the system. Use getsockname to
+	 * get its value.
+	 */
+	addr_len = sizeof(addr);
+	rc = getsockname(fd, (struct sockaddr *) &addr, &addr_len);
+	if (rc) {
+		DBG("getsockname failed: %s", strerror(errno));
+		goto error;
+	}
+
+	if (addr.sq_family != AF_QIPCRTR || addr_len != sizeof(addr)) {
+		DBG("Unexpected sockaddr from getsockname. family: %d size: %d",
+			addr.sq_family, addr_len);
+		rc = -EIO;
+		goto error;
+	}
+
+	addr.sq_port = QRTR_PORT_CTRL;
+	memset(&packet, 0, sizeof(packet));
+	packet.cmd = L_CPU_TO_LE32(QRTR_TYPE_NEW_LOOKUP);
+
+	bytes_written = sendto(fd, &packet,
+				sizeof(packet), 0,
+				(struct sockaddr *) &addr, addr_len);
+	if (bytes_written < 0) {
+		DBG("Failure sending data: %s", strerror(errno));
+		rc = -errno;
+		goto error;
+	}
+
+	l_util_hexdump(false, &packet, bytes_written,
+			device->debug_func, device->debug_data);
+
+	qrtr->discover_data = data;
+
+	return 0;
+
+error:
+	l_free(data);
+
+	return rc;
+}
+
+static void qmi_device_qrtr_destroy(struct qmi_device *device)
+{
+	struct qmi_device_qrtr *qrtr =
+		l_container_of(device, struct qmi_device_qrtr, super);
+
+	l_free(qrtr);
+}
+
+static const struct qmi_device_ops qrtr_ops = {
+	.write = NULL,
+	.discover = qmi_device_qrtr_discover,
+	.client_create = NULL,
+	.client_release = NULL,
+	.shutdown = NULL,
+	.destroy = qmi_device_qrtr_destroy,
+};
+
+struct qmi_device *qmi_device_new_qrtr(void)
+{
+	struct qmi_device_qrtr *qrtr;
+	int fd;
+
+	fd = socket(AF_QIPCRTR, SOCK_DGRAM, 0);
+	if (fd < 0)
+		return NULL;
+
+	qrtr = l_new(struct qmi_device_qrtr, 1);
+
+	if (qmi_device_init(&qrtr->super, fd, &qrtr_ops) < 0) {
+		close(fd);
+		l_free(qrtr);
+		return NULL;
+	}
+
+	l_io_set_read_handler(qrtr->super.io, received_qrtr_data, qrtr, NULL);
+
+	return &qrtr->super;
+}
+
 struct qmi_param *qmi_param_new(void)
 {
 	struct qmi_param *param;
diff --git a/drivers/qmimodem/qmi.h b/drivers/qmimodem/qmi.h
index 6a3d3415..506fed6b 100644
--- a/drivers/qmimodem/qmi.h
+++ b/drivers/qmimodem/qmi.h
@@ -101,6 +101,7 @@ bool qmi_device_set_expected_data_format(struct qmi_device *device,
 			enum qmi_device_expected_data_format format);
 
 struct qmi_device *qmi_device_new_qmux(const char *device);
+struct qmi_device *qmi_device_new_qrtr(void);
 
 struct qmi_param;
 
diff --git a/plugins/gobi.c b/plugins/gobi.c
index 99a7d556..cf78ad58 100644
--- a/plugins/gobi.c
+++ b/plugins/gobi.c
@@ -105,6 +105,7 @@ static int gobi_probe(struct ofono_modem *modem)
 	if (!data)
 		return -ENOMEM;
 
+#ifndef QMIMODEM_QRTR
 	kernel_driver = ofono_modem_get_string(modem, "KernelDriver");
 	DBG("kernel_driver: %s", kernel_driver);
 
@@ -117,6 +118,9 @@ static int gobi_probe(struct ofono_modem *modem)
 			ofono_modem_get_string(modem, "NetworkInterface"),
 			sizeof(data->main_net_name));
 	DBG("net: %s (%d)", data->main_net_name, data->main_net_ifindex);
+#else
+	(void) kernel_driver;
+#endif
 
 	ofono_modem_set_data(modem, data);
 
@@ -175,7 +179,10 @@ static void shutdown_device(struct ofono_modem *modem)
 
 	cleanup_services(data);
 
-	qmi_device_shutdown(data->device, shutdown_cb, modem, NULL);
+	if (qmi_device_shutdown(data->device, shutdown_cb, modem, NULL)) {
+		DBG("qmi_device_shutdown failed, calling shutdown_cb directly");
+		shutdown_cb(modem);
+	}
 }
 
 static void power_reset_cb(struct qmi_result *result, void *user_data)
@@ -426,11 +433,17 @@ static int gobi_enable(struct ofono_modem *modem)
 
 	DBG("%p", modem);
 
+#ifdef QMIMODEM_QRTR
+	(void) device;
+	data->device = qmi_device_new_qrtr();
+#else
 	device = ofono_modem_get_string(modem, "Device");
 	if (!device)
 		return -EINVAL;
 
 	data->device = qmi_device_new_qmux(device);
+#endif
+
 	if (!data->device)
 		return -EIO;
 
@@ -766,3 +779,28 @@ static struct ofono_modem_driver gobi_driver = {
 };
 
 OFONO_MODEM_DRIVER_BUILTIN(gobi, &gobi_driver)
+
+#ifdef QMIMODEM_QRTR
+static struct ofono_modem *qrtr_modem;
+
+static int gobi_init(void)
+{
+	DBG("");
+
+	qrtr_modem = ofono_modem_create(NULL, "gobi");
+	ofono_modem_register(qrtr_modem);
+
+	return 0;
+}
+
+static void gobi_exit(void)
+{
+	DBG("");
+
+	ofono_modem_remove(qrtr_modem);
+	qrtr_modem = NULL;
+}
+
+OFONO_PLUGIN_DEFINE(gobi, "Qualcomm Gobi modem driver", VERSION,
+			OFONO_PLUGIN_PRIORITY_DEFAULT, gobi_init, gobi_exit)
+#endif
