2 * uqmi -- tiny QMI support implementation
4 * Copyright (C) 2014-2015 Felix Fietkau <nbd@openwrt.org>
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.
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.
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.
28 #include "qmi-errors.h"
29 #include "qmi-errors.c"
32 bool cancel_all_requests
= false;
34 #define __qmi_service(_n) [__##_n] = _n
35 static const uint8_t qmi_services
[__QMI_SERVICE_LAST
] = {
41 void dump_packet(const char *prefix
, void *ptr
, int len
)
43 unsigned char *data
= ptr
;
46 fprintf(stderr
, "%s:", prefix
);
47 for (i
= 0; i
< len
; i
++)
48 fprintf(stderr
, " %02x", data
[i
]);
49 fprintf(stderr
, "\n");
54 qmi_get_service_idx(QmiService svc
)
58 for (i
= 0; i
< ARRAY_SIZE(qmi_services
); i
++)
59 if (qmi_services
[i
] == svc
)
65 static bool qmi_message_is_response(struct qmi_msg
*msg
)
67 if (msg
->qmux
.service
== QMI_SERVICE_CTL
) {
68 if (msg
->flags
& QMI_CTL_FLAG_RESPONSE
)
72 if (msg
->flags
& QMI_SERVICE_FLAG_RESPONSE
)
79 static bool qmi_message_is_indication(struct qmi_msg
*msg
)
81 if (msg
->qmux
.service
== QMI_SERVICE_CTL
) {
82 if (msg
->flags
& QMI_CTL_FLAG_INDICATION
)
86 if (msg
->flags
& QMI_SERVICE_FLAG_INDICATION
)
93 static void __qmi_request_complete(struct qmi_dev
*qmi
, struct qmi_request
*req
, struct qmi_msg
*msg
)
101 req
->pending
= false;
102 list_del(&req
->list
);
105 tlv_buf
= qmi_msg_get_tlv_buf(msg
, &tlv_len
);
106 req
->ret
= qmi_check_message_status(tlv_buf
, tlv_len
);
110 req
->ret
= QMI_ERROR_CANCELLED
;
113 if (req
->cb
&& (msg
|| !req
->no_error_cb
))
114 req
->cb(qmi
, req
, msg
);
117 *req
->complete
= true;
118 uloop_cancelled
= true;
122 static void qmi_process_msg(struct qmi_dev
*qmi
, struct qmi_msg
*msg
)
124 struct qmi_request
*req
;
127 if (qmi_message_is_indication(msg
)) {
128 if (msg
->qmux
.service
== QMI_SERVICE_CTL
) {
129 struct qmi_msg sync_msg
= {0};
130 qmi_set_ctl_sync_request(&sync_msg
);
131 /* A SYNC indication might be sent on boot in order to indicate
132 * that all Client IDs have been deallocated by the modem:
133 * cancel all requests, as they will not be answered. */
134 if (msg
->ctl
.message
== sync_msg
.ctl
.message
) {
135 while (!list_empty(&qmi
->req
)) {
136 req
= list_first_entry(&qmi
->req
, struct qmi_request
, list
);
137 qmi_request_cancel(qmi
, req
);
145 if (!qmi_message_is_response(msg
))
148 if (msg
->qmux
.service
== QMI_SERVICE_CTL
)
149 tid
= msg
->ctl
.transaction
;
151 tid
= le16_to_cpu(msg
->svc
.transaction
);
153 list_for_each_entry(req
, &qmi
->req
, list
) {
154 if (req
->service
!= msg
->qmux
.service
)
160 __qmi_request_complete(qmi
, req
, msg
);
165 static void qmi_notify_read(struct ustream
*us
, int bytes
)
167 struct qmi_dev
*qmi
= container_of(us
, struct qmi_dev
, sf
.stream
);
174 buf
= ustream_get_read_buf(us
, &len
);
178 dump_packet("Received packet", buf
, len
);
180 struct mbim_command_message
*mbim
= (void *) buf
;
182 if (len
< sizeof(*mbim
))
184 msg
= (struct qmi_msg
*) (buf
+ sizeof(*mbim
));
185 msg_len
= le32_to_cpu(mbim
->header
.length
);
186 if (!is_mbim_qmi(mbim
)) {
187 /* must consume other MBIM packets */
188 ustream_consume(us
, msg_len
);
192 if (len
< offsetof(struct qmi_msg
, flags
))
194 msg
= (struct qmi_msg
*) buf
;
195 msg_len
= le16_to_cpu(msg
->qmux
.len
) + 1;
201 qmi_process_msg(qmi
, msg
);
202 ustream_consume(us
, msg_len
);
206 int qmi_request_start(struct qmi_dev
*qmi
, struct qmi_request
*req
, request_cb cb
)
208 struct qmi_msg
*msg
= qmi
->buf
;
209 int len
= qmi_complete_request_message(msg
);
211 void *buf
= (void *) qmi
->buf
;
213 memset(req
, 0, sizeof(*req
));
215 req
->service
= msg
->qmux
.service
;
216 if (req
->service
== QMI_SERVICE_CTL
) {
217 tid
= qmi
->ctl_tid
++;
218 msg
->ctl
.transaction
= tid
;
220 int idx
= qmi_get_service_idx(req
->service
);
225 tid
= qmi
->service_data
[idx
].tid
++;
226 msg
->svc
.transaction
= cpu_to_le16(tid
);
227 msg
->qmux
.client
= qmi
->service_data
[idx
].client_id
;
233 list_add(&req
->list
, &qmi
->req
);
236 buf
-= sizeof(struct mbim_command_message
);
237 mbim_qmi_cmd((struct mbim_command_message
*) buf
, len
, tid
);
238 len
+= sizeof(struct mbim_command_message
);
241 dump_packet("Send packet", buf
, len
);
242 ustream_write(&qmi
->sf
.stream
, buf
, len
, false);
246 void qmi_request_cancel(struct qmi_dev
*qmi
, struct qmi_request
*req
)
249 __qmi_request_complete(qmi
, req
, NULL
);
252 /*! Run uloop until the request has been completed
254 * \param qmi the qmi device
255 * \param req the request to wait for
256 * \return req->ret (0 on success)
258 int qmi_request_wait(struct qmi_dev
*qmi
, struct qmi_request
*req
)
260 bool complete
= false;
267 *req
->complete
= true;
269 req
->complete
= &complete
;
271 cancelled
= uloop_cancelled
;
272 uloop_cancelled
= false;
275 if (cancel_all_requests
)
276 qmi_request_cancel(qmi
, req
);
278 uloop_cancelled
= cancelled
;
281 if (req
->complete
== &complete
)
282 req
->complete
= NULL
;
287 struct qmi_connect_request
{
288 struct qmi_request req
;
292 static void qmi_connect_service_cb(struct qmi_dev
*qmi
, struct qmi_request
*req
, struct qmi_msg
*msg
)
294 struct qmi_ctl_allocate_cid_response res
;
295 struct qmi_connect_request
*creq
= container_of(req
, struct qmi_connect_request
, req
);
300 qmi_parse_ctl_allocate_cid_response(msg
, &res
);
301 creq
->cid
= res
.data
.allocation_info
.cid
;
304 int qmi_service_connect(struct qmi_dev
*qmi
, QmiService svc
, int client_id
)
306 struct qmi_ctl_allocate_cid_request creq
= {
307 QMI_INIT(service
, svc
)
309 struct qmi_connect_request req
;
310 int idx
= qmi_get_service_idx(svc
);
311 struct qmi_msg
*msg
= qmi
->buf
;
316 if (qmi
->service_connected
& (1 << idx
))
320 qmi_set_ctl_allocate_cid_request(msg
, &creq
);
321 qmi_request_start(qmi
, &req
.req
, qmi_connect_service_cb
);
322 qmi_request_wait(qmi
, &req
.req
);
329 qmi
->service_keep_cid
|= (1 << idx
);
332 qmi
->service_data
[idx
].connected
= true;
333 qmi
->service_data
[idx
].client_id
= client_id
;
334 qmi
->service_data
[idx
].tid
= 1;
335 qmi
->service_connected
|= (1 << idx
);
340 static void __qmi_service_disconnect(struct qmi_dev
*qmi
, int idx
)
342 int client_id
= qmi
->service_data
[idx
].client_id
;
343 struct qmi_ctl_release_cid_request creq
= {
344 QMI_INIT_SEQUENCE(release_info
,
345 .service
= qmi_services
[idx
],
349 struct qmi_request req
;
350 struct qmi_msg
*msg
= qmi
->buf
;
352 qmi
->service_connected
&= ~(1 << idx
);
353 qmi
->service_data
[idx
].client_id
= -1;
354 qmi
->service_data
[idx
].tid
= 0;
356 qmi_set_ctl_release_cid_request(msg
, &creq
);
357 qmi_request_start(qmi
, &req
, NULL
);
358 qmi_request_wait(qmi
, &req
);
361 int qmi_service_release_client_id(struct qmi_dev
*qmi
, QmiService svc
)
363 int idx
= qmi_get_service_idx(svc
);
364 qmi
->service_release_cid
|= 1 << idx
;
368 static void qmi_close_all_services(struct qmi_dev
*qmi
)
370 uint32_t connected
= qmi
->service_connected
;
373 qmi
->service_keep_cid
&= ~qmi
->service_release_cid
;
374 for (idx
= 0; connected
; idx
++, connected
>>= 1) {
375 if (!(connected
& 1))
378 if (qmi
->service_keep_cid
& (1 << idx
))
381 __qmi_service_disconnect(qmi
, idx
);
385 int qmi_service_get_client_id(struct qmi_dev
*qmi
, QmiService svc
)
387 int idx
= qmi_get_service_idx(svc
);
392 qmi
->service_keep_cid
|= (1 << idx
);
393 return qmi
->service_data
[idx
].client_id
;
396 int qmi_device_open(struct qmi_dev
*qmi
, const char *path
)
399 struct mbim_command_message mbim
;
405 struct ustream
*us
= &qmi
->sf
.stream
;
410 fd
= open(path
, O_RDWR
| O_EXCL
| O_NONBLOCK
| O_NOCTTY
);
414 us
->notify_read
= qmi_notify_read
;
415 ustream_fd_init(&qmi
->sf
, fd
);
416 INIT_LIST_HEAD(&qmi
->req
);
418 qmi
->buf
= msgbuf
.u
.buf
;
423 void qmi_device_close(struct qmi_dev
*qmi
)
425 struct qmi_request
*req
;
427 qmi_close_all_services(qmi
);
428 ustream_free(&qmi
->sf
.stream
);
429 close(qmi
->sf
.fd
.fd
);
431 while (!list_empty(&qmi
->req
)) {
432 req
= list_first_entry(&qmi
->req
, struct qmi_request
, list
);
433 qmi_request_cancel(qmi
, req
);
437 QmiService
qmi_service_get_by_name(const char *str
)
439 static const struct {
443 { "dms", QMI_SERVICE_DMS
},
444 { "nas", QMI_SERVICE_NAS
},
445 { "pds", QMI_SERVICE_PDS
},
446 { "wds", QMI_SERVICE_WDS
},
447 { "wms", QMI_SERVICE_WMS
},
448 { "wda", QMI_SERVICE_WDA
},
449 { "uim", QMI_SERVICE_UIM
},
453 for (i
= 0; i
< ARRAY_SIZE(services
); i
++) {
454 if (!strcasecmp(str
, services
[i
].name
))
455 return services
[i
].svc
;
461 const char *qmi_get_error_str(int code
)
465 for (i
= 0; i
< ARRAY_SIZE(qmi_errors
); i
++) {
466 if (qmi_errors
[i
].code
== code
)
467 return qmi_errors
[i
].text
;
470 return "Unknown error";