7 #include "qmi-errors.h"
8 #include "qmi-errors.c"
10 bool cancel_all_requests
= false;
12 #define __qmi_service(_n) [__##_n] = _n
13 static const uint8_t qmi_services
[__QMI_SERVICE_LAST
] = {
24 void dump_packet(const char *prefix
, void *ptr
, int len
)
26 unsigned char *data
= ptr
;
29 fprintf(stderr
, "%s:", prefix
);
30 for (i
= 0; i
< len
; i
++)
31 fprintf(stderr
, " %02x", data
[i
]);
32 fprintf(stderr
, "\n");
37 qmi_get_service_idx(QmiService svc
)
41 for (i
= 0; i
< ARRAY_SIZE(qmi_services
); i
++)
42 if (qmi_services
[i
] == svc
)
48 static void __qmi_request_complete(struct qmi_dev
*qmi
, struct qmi_request
*req
, struct qmi_msg
*msg
)
61 tlv_buf
= qmi_msg_get_tlv_buf(msg
, &tlv_len
);
62 req
->ret
= qmi_check_message_status(tlv_buf
, tlv_len
);
66 req
->ret
= QMI_ERROR_CANCELLED
;
69 if (req
->cb
&& (msg
|| !req
->no_error_cb
))
70 req
->cb(qmi
, req
, msg
);
73 *req
->complete
= true;
74 uloop_cancelled
= true;
78 static void qmi_process_msg(struct qmi_dev
*qmi
, struct qmi_msg
*msg
)
80 struct qmi_request
*req
;
83 if (msg
->qmux
.service
== QMI_SERVICE_CTL
)
84 tid
= msg
->ctl
.transaction
;
86 tid
= le16_to_cpu(msg
->svc
.transaction
);
88 list_for_each_entry(req
, &qmi
->req
, list
) {
89 if (req
->service
!= msg
->qmux
.service
)
95 __qmi_request_complete(qmi
, req
, msg
);
100 static void qmi_notify_read(struct ustream
*us
, int bytes
)
102 struct qmi_dev
*qmi
= container_of(us
, struct qmi_dev
, sf
.stream
);
109 buf
= ustream_get_read_buf(us
, &len
);
113 if (len
< offsetof(struct qmi_msg
, flags
))
116 msg
= (struct qmi_msg
*) buf
;
117 msg_len
= le16_to_cpu(msg
->qmux
.len
) + 1;
121 dump_packet("Received packet", msg
, msg_len
);
122 qmi_process_msg(qmi
, msg
);
123 ustream_consume(us
, msg_len
);
127 int qmi_request_start(struct qmi_dev
*qmi
, struct qmi_request
*req
, struct qmi_msg
*msg
, request_cb cb
)
129 int len
= qmi_complete_request_message(msg
);
134 memset(req
, 0, sizeof(*req
));
136 req
->service
= msg
->qmux
.service
;
137 if (req
->service
== QMI_SERVICE_CTL
) {
138 tid
= qmi
->ctl_tid
++;
139 msg
->ctl
.transaction
= tid
;
141 int idx
= qmi_get_service_idx(req
->service
);
146 tid
= qmi
->service_data
[idx
].tid
++;
147 msg
->svc
.transaction
= cpu_to_le16(tid
);
148 msg
->qmux
.client
= qmi
->service_data
[idx
].client_id
;
154 list_add(&req
->list
, &qmi
->req
);
156 dump_packet("Send packet", msg
, len
);
157 ustream_write(&qmi
->sf
.stream
, (void *) msg
, len
, false);
161 void qmi_request_cancel(struct qmi_dev
*qmi
, struct qmi_request
*req
)
164 __qmi_request_complete(qmi
, req
, NULL
);
167 int qmi_request_wait(struct qmi_dev
*qmi
, struct qmi_request
*req
)
169 bool complete
= false;
176 *req
->complete
= true;
178 req
->complete
= &complete
;
180 cancelled
= uloop_cancelled
;
181 uloop_cancelled
= false;
184 if (cancel_all_requests
)
185 qmi_request_cancel(qmi
, req
);
187 uloop_cancelled
= cancelled
;
190 if (req
->complete
== &complete
)
191 req
->complete
= NULL
;
196 struct qmi_connect_request
{
197 struct qmi_request req
;
201 static void qmi_connect_service_cb(struct qmi_dev
*qmi
, struct qmi_request
*req
, struct qmi_msg
*msg
)
203 struct qmi_ctl_allocate_cid_response res
;
204 struct qmi_connect_request
*creq
= container_of(req
, struct qmi_connect_request
, req
);
209 qmi_parse_ctl_allocate_cid_response(msg
, &res
);
210 creq
->cid
= res
.data
.allocation_info
.cid
;
213 int qmi_service_connect(struct qmi_dev
*qmi
, QmiService svc
, int client_id
)
215 struct qmi_ctl_allocate_cid_request creq
= {
216 QMI_INIT(service
, svc
)
218 struct qmi_connect_request req
;
219 int idx
= qmi_get_service_idx(svc
);
220 struct qmi_msg
*msg
= &msgbuf
.msg
;
225 if (qmi
->service_connected
& (1 << idx
))
229 qmi_set_ctl_allocate_cid_request(msg
, &creq
);
230 qmi_request_start(qmi
, &req
.req
, msg
, qmi_connect_service_cb
);
231 qmi_request_wait(qmi
, &req
.req
);
239 qmi
->service_data
[idx
].connected
= true;
240 qmi
->service_data
[idx
].client_id
= client_id
;
241 qmi
->service_data
[idx
].tid
= 1;
242 qmi
->service_connected
|= (1 << idx
);
247 static void __qmi_service_disconnect(struct qmi_dev
*qmi
, int idx
)
249 int client_id
= qmi
->service_data
[idx
].client_id
;
250 struct qmi_ctl_release_cid_request creq
= {
251 QMI_INIT_SEQUENCE(release_info
,
252 .service
= qmi_services
[idx
],
256 struct qmi_request req
;
257 struct qmi_msg
*msg
= &msgbuf
.msg
;
259 qmi
->service_connected
&= ~(1 << idx
);
260 qmi
->service_data
[idx
].client_id
= -1;
261 qmi
->service_data
[idx
].tid
= 0;
263 qmi_set_ctl_release_cid_request(msg
, &creq
);
264 qmi_request_start(qmi
, &req
, msg
, NULL
);
265 qmi_request_wait(qmi
, &req
);
268 static void qmi_close_all_services(struct qmi_dev
*qmi
)
270 uint32_t connected
= qmi
->service_connected
;
273 for (idx
= 0; connected
; idx
++, connected
>>= 1) {
274 if (!(connected
& 1))
277 if (qmi
->service_keep_cid
& (1 << idx
))
280 __qmi_service_disconnect(qmi
, idx
);
284 int qmi_service_get_client_id(struct qmi_dev
*qmi
, QmiService svc
)
286 int idx
= qmi_get_service_idx(svc
);
291 qmi
->service_keep_cid
|= (1 << idx
);
292 return qmi
->service_data
[idx
].client_id
;
295 int qmi_device_open(struct qmi_dev
*qmi
, const char *path
)
297 struct ustream
*us
= &qmi
->sf
.stream
;
302 fd
= open(path
, O_RDWR
| O_EXCL
| O_NONBLOCK
| O_NOCTTY
);
306 us
->notify_read
= qmi_notify_read
;
307 ustream_fd_init(&qmi
->sf
, fd
);
308 INIT_LIST_HEAD(&qmi
->req
);
314 void qmi_device_close(struct qmi_dev
*qmi
)
316 struct qmi_request
*req
;
319 qmi_close_all_services(qmi
);
320 ustream_free(&qmi
->sf
.stream
);
321 close(qmi
->sf
.fd
.fd
);
323 while (!list_empty(&qmi
->req
)) {
324 req
= list_first_entry(&qmi
->req
, struct qmi_request
, list
);
325 qmi_request_cancel(qmi
, req
);
329 QmiService
qmi_service_get_by_name(const char *str
)
331 static const struct {
335 { "dms", QMI_SERVICE_DMS
},
336 { "nas", QMI_SERVICE_NAS
},
337 { "pds", QMI_SERVICE_PDS
},
338 { "wds", QMI_SERVICE_WDS
},
339 { "wms", QMI_SERVICE_WMS
},
343 for (i
= 0; i
< ARRAY_SIZE(services
); i
++) {
344 if (!strcasecmp(str
, services
[i
].name
))
345 return services
[i
].svc
;
351 const char *qmi_get_error_str(int code
)
355 for (i
= 0; i
< ARRAY_SIZE(qmi_errors
); i
++) {
356 if (qmi_errors
[i
].code
== code
)
357 return qmi_errors
[i
].text
;
360 return "Unknown error";