struct qmi_service *service;
struct qmi_request *req;
uint16_t tid;
+ bool resp, ind;
- /* FIXME: indications? */
-
- if (msg->qmux.service == QMI_SERVICE_CTL)
+ if (msg->qmux.service == QMI_SERVICE_CTL) {
modem_log(qmi->modem, LOGL_DEBUG, "Process message from srv %d msg %04x flag: %02x tid: %02x",
msg->qmux.service, le16_to_cpu(msg->ctl.message), msg->flags, msg->ctl.transaction);
- else
+ tid = msg->ctl.transaction;
+ ind = msg->flags & QMI_CTL_FLAG_INDICATION;
+ resp = msg->flags & QMI_CTL_FLAG_RESPONSE;
+ if (!ind && !resp) {
+ /* TODO: error_log("Invalid message received") */
+ return;
+ }
+ } else {
modem_log(qmi->modem, LOGL_DEBUG, "Process message from srv %d msg %04x flag: %02x tid: %04x",
msg->qmux.service, le16_to_cpu(msg->svc.message), msg->flags, msg->svc.transaction);
-
- if (msg->flags != QMI_CTL_FLAG_RESPONSE && msg->flags != QMI_SERVICE_FLAG_RESPONSE)
- return;
-
- if (msg->qmux.service == QMI_SERVICE_CTL)
- tid = msg->ctl.transaction;
- else
tid = le16_to_cpu(msg->svc.transaction);
+ ind = msg->flags & QMI_SERVICE_FLAG_INDICATION;
+ resp = msg->flags & QMI_SERVICE_FLAG_RESPONSE;
+ if (!ind && !resp) {
+ /* TODO: error_log("Invalid message received") */
+ return;
+ }
+ }
service = uqmi_service_find(qmi, msg->qmux.service);
if (!service) {
return;
}
- list_for_each_entry(req, &service->reqs, list) {
- if (req->tid != tid)
- continue;
+ /* Hopefully an indication *and* response isn't possible */
+ if (ind) {
+ uqmi_service_handle_indication(service, msg);
+ }
- __qmi_request_complete(service, req, msg);
- return;
+ if (resp) {
+ list_for_each_entry(req, &service->reqs, list) {
+ if (req->tid != tid)
+ continue;
+
+ __qmi_request_complete(service, req, msg);
+ return;
+ }
}
/* error_log("Couldn't find a tid for incoming message") */
uloop_timeout_set(&qmi->shutdown, timeout_ms);
}
}
-
service->client_id = -1;
list_add(&service->list, &qmi->services);
+ INIT_LIST_HEAD(&service->indications);
INIT_LIST_HEAD(&service->reqs);
return service;
/* Control service is special */
uqmi_ctrl_release_clientid(service);
}
+
+int uqmi_service_register_indication(struct qmi_service *service, uint16_t qmi_ind, indication_cb cb, void *cb_data)
+{
+ struct qmi_indication *indication;
+
+ indication = talloc_zero(service, struct qmi_indication);
+ if (!indication)
+ return 1;
+
+ indication->cb = cb;
+ indication->cb_data = cb_data;
+ indication->qmi_ind = qmi_ind;
+ list_add(&indication->list, &service->indications);
+
+ return 0;
+}
+
+int uqmi_service_remove_indication(struct qmi_service *service, uint16_t qmi_ind, indication_cb cb, void *cb_data)
+{
+ struct qmi_indication *indication, *tmp;
+
+ list_for_each_entry_safe(indication, tmp, &service->indications, list) {
+ if (qmi_ind != indication->qmi_ind)
+ continue;
+
+ if (indication->cb != cb)
+ continue;
+
+ if (indication->cb_data != cb_data)
+ continue;
+
+ list_del(&indication->list);
+ }
+
+ return 0;
+}
+
+int uqmi_service_handle_indication(struct qmi_service *service, struct qmi_msg *msg)
+{
+ uint16_t qmi_ind;
+ bool handled = false;
+ struct qmi_indication *indication, *tmp;
+
+ if (msg->qmux.service == QMI_SERVICE_CTL)
+ qmi_ind = msg->ctl.message;
+ else
+ qmi_ind = msg->svc.message;
+
+
+ list_for_each_entry_safe(indication, tmp, &service->indications, list) {
+ if (qmi_ind != indication->qmi_ind)
+ continue;
+
+ if (indication->cb) {
+ indication->cb(service, msg, indication->cb_data);
+ handled = true;
+ }
+ }
+
+ return handled ? 0 : 1;
+}
/* contains all pending requests */
struct list_head reqs;
+
+ /* contains indication registers
+ * a sorted llist by qmi msg id
+ */
+ struct list_head indications;
+};
+
+typedef void (*indication_cb)(struct qmi_service *service, struct qmi_msg *msg, void *data);
+struct qmi_indication {
+ /* a sorted linked list by qmi_ind in indications */
+ struct list_head list;
+ /* qmi_ind the qmi message id.
+ * Multiple callbacks can receive the same indication
+ */
+ uint16_t qmi_ind;
+ indication_cb cb;
+ void *cb_data;
};
struct qmi_service *uqmi_service_find(struct qmi_dev *qmi, int service_id);
void uqmi_service_get_client_id_cb(struct qmi_service *service, uint16_t client_id);
void uqmi_service_close_cb(struct qmi_service *service);
+/* indications */
+
+int uqmi_service_register_indication(struct qmi_service *service, uint16_t indication, indication_cb cb, void *cb_data);
+int uqmi_service_remove_indication(struct qmi_service *service, uint16_t indication, indication_cb cb, void *cb_data);
+int uqmi_service_handle_indication(struct qmi_service *service, struct qmi_msg *msg);
+
#endif /* __UQMID_QMI_DEV_H */