Many Qualcomm based devices offer a QMI service when running in MBIM
mode. This is useful for a number of requests which have no MBIM
counterpart.
This implementation is very basic, simply wrapping the QMI requests
in an MBIM command. It is up to the user to make sure that the MBIM
command sequence is valid, using a mix of umbim and uqmi requests.
umbim must be used to send "OPEN" before uqmi can issue any MBIM
requests. Example:
1. use umbim to open the session, using the '-n' option:
$ umbim -d /dev/cdc-wdm0 -n caps
devicetype: 0003 - remote
cellularclass: 0001
voiceclass: 0001 - no-voice
simclass: 0002
dataclass: 003C
smscaps: 0003
controlcaps: 0001
maxsessions: 0008
deviceid: 0145820007xxxxx
firmwareinfo: SWI9X30C_02.08.02.00
hardwareinfo: EM7455
2. use uqmi to send an MBIM request, using the '-m' option:
$ uqmi -m -d /dev/cdc-wdm0 --get-serving-system
{
"registration": "registered",
"plmn_mcc": 242,
"plmn_mnc": 1,
"plmn_description": "TELENOR",
"roaming": false
}
3. use umbim to close the open session, using the '-t X' option:
$ umbim -d /dev/cdc-wdm0 -t 2 caps
devicetype: 0003 - remote
cellularclass: 0001
voiceclass: 0001 - no-voice
simclass: 0002
dataclass: 003C
smscaps: 0003
controlcaps: 0001
maxsessions: 0008
deviceid: 0145820007xxxxx
firmwareinfo: SWI9X30C_02.08.02.00
hardwareinfo: EM7455
Signed-off-by: Bjørn Mork <[email protected]>
Signed-off-by: Felix Fietkau <[email protected]> [cleanup, portability fixes]
SET(CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "")
-SET(SOURCES main.c dev.c commands.c qmi-message.c)
+SET(SOURCES main.c dev.c commands.c qmi-message.c mbim.c)
FIND_PATH(ubox_include_dir libubox/usock.h)
FIND_PATH(blobmsg_json_include_dir libubox/blobmsg_json.h)
#include "uqmi.h"
#include "qmi-errors.h"
#include "qmi-errors.c"
+#include "mbim.h"
bool cancel_all_requests = false;
};
#undef __qmi_service
-static union {
- char buf[512];
- struct qmi_msg msg;
-} msgbuf;
+static struct {
+ struct mbim_command_message mbim;
+ union {
+ char buf[512];
+ struct qmi_msg msg;
+ } u;
+} __packed msgbuf;
#ifdef DEBUG_PACKET
void dump_packet(const char *prefix, void *ptr, int len)
char *buf;
int len, msg_len;
+
while (1) {
buf = ustream_get_read_buf(us, &len);
if (!buf || !len)
return;
- if (len < offsetof(struct qmi_msg, flags))
- return;
+ dump_packet("Received packet", buf, len);
+ if (qmi->is_mbim) {
+ struct mbim_command_message *mbim = (void *) buf;
+
+ if (len < sizeof(*mbim))
+ return;
+ msg = (struct qmi_msg *) (buf + sizeof(*mbim));
+ msg_len = le32_to_cpu(mbim->header.length);
+ if (!is_mbim_qmi(mbim)) {
+ /* must consume other MBIM packets */
+ ustream_consume(us, msg_len);
+ return;
+ }
+ } else {
+ if (len < offsetof(struct qmi_msg, flags))
+ return;
+ msg = (struct qmi_msg *) buf;
+ msg_len = le16_to_cpu(msg->qmux.len) + 1;
+ }
- msg = (struct qmi_msg *) buf;
- msg_len = le16_to_cpu(msg->qmux.len) + 1;
if (len < msg_len)
return;
- dump_packet("Received packet", msg, msg_len);
qmi_process_msg(qmi, msg);
ustream_consume(us, msg_len);
}
{
int len = qmi_complete_request_message(msg);
uint16_t tid;
+ char *buf = (void *) msg;
memset(req, 0, sizeof(*req));
req->ret = -1;
req->pending = true;
list_add(&req->list, &qmi->req);
- dump_packet("Send packet", msg, len);
- ustream_write(&qmi->sf.stream, (void *) msg, len, false);
+ if (qmi->is_mbim) {
+ buf -= sizeof(struct mbim_command_message);
+ mbim_qmi_cmd((struct mbim_command_message *) buf, len, tid);
+ len += sizeof(struct mbim_command_message);
+ }
+
+ dump_packet("Send packet", buf, len);
+ ustream_write(&qmi->sf.stream, buf, len, false);
return 0;
}
};
struct qmi_connect_request req;
int idx = qmi_get_service_idx(svc);
- struct qmi_msg *msg = &msgbuf.msg;
+ struct qmi_msg *msg = &msgbuf.u.msg;
if (idx < 0)
return -1;
)
};
struct qmi_request req;
- struct qmi_msg *msg = &msgbuf.msg;
+ struct qmi_msg *msg = &msgbuf.u.msg;
qmi->service_connected &= ~(1 << idx);
qmi->service_data[idx].client_id = -1;
{ "device", required_argument, NULL, 'd' },
{ "keep-client-id", required_argument, NULL, 'k' },
{ "release-client-id", required_argument, NULL, 'r' },
+ { "mbim", no_argument, NULL, 'm' },
{ NULL, 0, NULL, 0 }
};
#undef __uqmi_command
" --device=NAME, -d NAME: Set device name to NAME (required)\n"
" --keep-client-id <name>: Keep Client ID for service <name>\n"
" --release-client-id <name>: Release Client ID after exiting\n"
+ " --mbim, -m NAME is an MBIM device with EXT_QMUX support\n"
"\n"
"Services: dms, nas, pds, wds, wms\n"
"\n"
signal(SIGINT, handle_exit_signal);
signal(SIGTERM, handle_exit_signal);
- while ((ch = getopt_long(argc, argv, "d:k:s", uqmi_getopt, NULL)) != -1) {
+ while ((ch = getopt_long(argc, argv, "d:k:sm", uqmi_getopt, NULL)) != -1) {
int cmd_opt = CMD_OPT(ch);
if (ch < 0 && cmd_opt >= 0 && cmd_opt < __UQMI_COMMAND_LAST) {
case 's':
single_line = true;
break;
+ case 'm':
+ dev.is_mbim = true;
+ break;
default:
return usage(argv[0]);
}
--- /dev/null
+/*
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ */
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <string.h>
+
+#include <stdio.h>
+
+#include "mbim.h"
+
+static const uint8_t qmiuuid[16] = { 0xd1, 0xa3, 0x0b, 0xc2, 0xf9, 0x7a, 0x6e, 0x43,
+ 0xbf, 0x65, 0xc7, 0xe2, 0x4f, 0xb0, 0xf0, 0xd3 };
+
+bool is_mbim_qmi(struct mbim_command_message *msg)
+{
+ return msg->header.type == cpu_to_le32(MBIM_MESSAGE_TYPE_COMMAND_DONE) &&
+ msg->command_id == cpu_to_le32(MBIM_CID_QMI_MSG) &&
+ !msg->command_type && /* actually 'status' here */
+ !memcmp(msg->service_id, qmiuuid, 16);
+ }
+
+void mbim_qmi_cmd(struct mbim_command_message *msg, int len, uint16_t tid)
+{
+ msg->header.type = cpu_to_le32(MBIM_MESSAGE_TYPE_COMMAND);
+ msg->header.length = sizeof(*msg) + len;
+ msg->header.transaction_id = cpu_to_le32(tid);
+ msg->fragment_header.total = 1;
+ msg->fragment_header.current = 0;
+ memcpy(msg->service_id, qmiuuid, 16);
+ msg->command_id = cpu_to_le32(MBIM_CID_QMI_MSG);
+ msg->command_type = cpu_to_le32(MBIM_MESSAGE_COMMAND_TYPE_SET);
+ msg->buffer_length = len;
+}
--- /dev/null
+/*
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ */
+
+#ifndef _MBIM_H__
+#define _MBIM_H__
+
+#include <libubox/utils.h>
+#include <stdbool.h>
+#include <stdint.h>
+
+#define MBIM_MESSAGE_TYPE_COMMAND 0x00000003
+#define MBIM_MESSAGE_TYPE_COMMAND_DONE 0x80000003
+#define MBIM_MESSAGE_COMMAND_TYPE_SET 1
+#define MBIM_CID_QMI_MSG 1
+
+struct mbim_message_header {
+ uint32_t type;
+ uint32_t length;
+ uint32_t transaction_id;
+} __packed;
+
+struct mbim_fragment_header {
+ uint32_t total;
+ uint32_t current;
+} __packed;
+
+struct mbim_command_message {
+ struct mbim_message_header header;
+ struct mbim_fragment_header fragment_header;
+ uint8_t service_id[16];
+ uint32_t command_id;
+ uint32_t command_type;
+ uint32_t buffer_length;
+} __packed;
+
+bool is_mbim_qmi(struct mbim_command_message *msg);
+void mbim_qmi_cmd(struct mbim_command_message *msg, int len, uint16_t tid);
+
+#endif
uint32_t service_release_cid;
uint8_t ctl_tid;
+
+ bool is_mbim;
};
struct qmi_request {