dev.c 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419
  1. /*
  2. * uqmi -- tiny QMI support implementation
  3. *
  4. * Copyright (C) 2014-2015 Felix Fietkau <nbd@openwrt.org>
  5. *
  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.
  10. *
  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.
  15. *
  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.
  20. */
  21. #include <fcntl.h>
  22. #include <unistd.h>
  23. #include <stdlib.h>
  24. #include <stdio.h>
  25. #include <string.h>
  26. #include "uqmi.h"
  27. #include "qmi-errors.h"
  28. #include "qmi-errors.c"
  29. #include "mbim.h"
  30. bool cancel_all_requests = false;
  31. #define __qmi_service(_n) [__##_n] = _n
  32. static const uint8_t qmi_services[__QMI_SERVICE_LAST] = {
  33. __qmi_services
  34. };
  35. #undef __qmi_service
  36. #ifdef DEBUG_PACKET
  37. void dump_packet(const char *prefix, void *ptr, int len)
  38. {
  39. unsigned char *data = ptr;
  40. int i;
  41. fprintf(stderr, "%s:", prefix);
  42. for (i = 0; i < len; i++)
  43. fprintf(stderr, " %02x", data[i]);
  44. fprintf(stderr, "\n");
  45. }
  46. #endif
  47. static int
  48. qmi_get_service_idx(QmiService svc)
  49. {
  50. int i;
  51. for (i = 0; i < ARRAY_SIZE(qmi_services); i++)
  52. if (qmi_services[i] == svc)
  53. return i;
  54. return -1;
  55. }
  56. static void __qmi_request_complete(struct qmi_dev *qmi, struct qmi_request *req, struct qmi_msg *msg)
  57. {
  58. void *tlv_buf;
  59. int tlv_len;
  60. if (!req->pending)
  61. return;
  62. req->pending = false;
  63. list_del(&req->list);
  64. if (msg) {
  65. tlv_buf = qmi_msg_get_tlv_buf(msg, &tlv_len);
  66. req->ret = qmi_check_message_status(tlv_buf, tlv_len);
  67. if (req->ret)
  68. msg = NULL;
  69. } else {
  70. req->ret = QMI_ERROR_CANCELLED;
  71. }
  72. if (req->cb && (msg || !req->no_error_cb))
  73. req->cb(qmi, req, msg);
  74. if (req->complete) {
  75. *req->complete = true;
  76. uloop_cancelled = true;
  77. }
  78. }
  79. static void qmi_process_msg(struct qmi_dev *qmi, struct qmi_msg *msg)
  80. {
  81. struct qmi_request *req;
  82. uint16_t tid;
  83. if (msg->flags != QMI_CTL_FLAG_RESPONSE && msg->flags != QMI_SERVICE_FLAG_RESPONSE)
  84. return;
  85. if (msg->qmux.service == QMI_SERVICE_CTL)
  86. tid = msg->ctl.transaction;
  87. else
  88. tid = le16_to_cpu(msg->svc.transaction);
  89. list_for_each_entry(req, &qmi->req, list) {
  90. if (req->service != msg->qmux.service)
  91. continue;
  92. if (req->tid != tid)
  93. continue;
  94. __qmi_request_complete(qmi, req, msg);
  95. return;
  96. }
  97. }
  98. static void qmi_notify_read(struct ustream *us, int bytes)
  99. {
  100. struct qmi_dev *qmi = container_of(us, struct qmi_dev, sf.stream);
  101. struct qmi_msg *msg;
  102. char *buf;
  103. int len, msg_len;
  104. while (1) {
  105. buf = ustream_get_read_buf(us, &len);
  106. if (!buf || !len)
  107. return;
  108. dump_packet("Received packet", buf, len);
  109. if (qmi->is_mbim) {
  110. struct mbim_command_message *mbim = (void *) buf;
  111. if (len < sizeof(*mbim))
  112. return;
  113. msg = (struct qmi_msg *) (buf + sizeof(*mbim));
  114. msg_len = le32_to_cpu(mbim->header.length);
  115. if (!is_mbim_qmi(mbim)) {
  116. /* must consume other MBIM packets */
  117. ustream_consume(us, msg_len);
  118. return;
  119. }
  120. } else {
  121. if (len < offsetof(struct qmi_msg, flags))
  122. return;
  123. msg = (struct qmi_msg *) buf;
  124. msg_len = le16_to_cpu(msg->qmux.len) + 1;
  125. }
  126. if (len < msg_len)
  127. return;
  128. qmi_process_msg(qmi, msg);
  129. ustream_consume(us, msg_len);
  130. }
  131. }
  132. int qmi_request_start(struct qmi_dev *qmi, struct qmi_request *req, request_cb cb)
  133. {
  134. struct qmi_msg *msg = qmi->buf;
  135. int len = qmi_complete_request_message(msg);
  136. uint16_t tid;
  137. void *buf = (void *) qmi->buf;
  138. memset(req, 0, sizeof(*req));
  139. req->ret = -1;
  140. req->service = msg->qmux.service;
  141. if (req->service == QMI_SERVICE_CTL) {
  142. tid = qmi->ctl_tid++;
  143. msg->ctl.transaction = tid;
  144. } else {
  145. int idx = qmi_get_service_idx(req->service);
  146. if (idx < 0)
  147. return -1;
  148. tid = qmi->service_data[idx].tid++;
  149. msg->svc.transaction = cpu_to_le16(tid);
  150. msg->qmux.client = qmi->service_data[idx].client_id;
  151. }
  152. req->tid = tid;
  153. req->cb = cb;
  154. req->pending = true;
  155. list_add(&req->list, &qmi->req);
  156. if (qmi->is_mbim) {
  157. buf -= sizeof(struct mbim_command_message);
  158. mbim_qmi_cmd((struct mbim_command_message *) buf, len, tid);
  159. len += sizeof(struct mbim_command_message);
  160. }
  161. dump_packet("Send packet", buf, len);
  162. ustream_write(&qmi->sf.stream, buf, len, false);
  163. return 0;
  164. }
  165. void qmi_request_cancel(struct qmi_dev *qmi, struct qmi_request *req)
  166. {
  167. req->cb = NULL;
  168. __qmi_request_complete(qmi, req, NULL);
  169. }
  170. int qmi_request_wait(struct qmi_dev *qmi, struct qmi_request *req)
  171. {
  172. bool complete = false;
  173. bool cancelled;
  174. if (!req->pending)
  175. return req->ret;
  176. if (req->complete)
  177. *req->complete = true;
  178. req->complete = &complete;
  179. while (!complete) {
  180. cancelled = uloop_cancelled;
  181. uloop_cancelled = false;
  182. uloop_run();
  183. if (cancel_all_requests)
  184. qmi_request_cancel(qmi, req);
  185. uloop_cancelled = cancelled;
  186. }
  187. if (req->complete == &complete)
  188. req->complete = NULL;
  189. return req->ret;
  190. }
  191. struct qmi_connect_request {
  192. struct qmi_request req;
  193. int cid;
  194. };
  195. static void qmi_connect_service_cb(struct qmi_dev *qmi, struct qmi_request *req, struct qmi_msg *msg)
  196. {
  197. struct qmi_ctl_allocate_cid_response res;
  198. struct qmi_connect_request *creq = container_of(req, struct qmi_connect_request, req);
  199. if (!msg)
  200. return;
  201. qmi_parse_ctl_allocate_cid_response(msg, &res);
  202. creq->cid = res.data.allocation_info.cid;
  203. }
  204. int qmi_service_connect(struct qmi_dev *qmi, QmiService svc, int client_id)
  205. {
  206. struct qmi_ctl_allocate_cid_request creq = {
  207. QMI_INIT(service, svc)
  208. };
  209. struct qmi_connect_request req;
  210. int idx = qmi_get_service_idx(svc);
  211. struct qmi_msg *msg = qmi->buf;
  212. if (idx < 0)
  213. return -1;
  214. if (qmi->service_connected & (1 << idx))
  215. return 0;
  216. if (client_id < 0) {
  217. qmi_set_ctl_allocate_cid_request(msg, &creq);
  218. qmi_request_start(qmi, &req.req, qmi_connect_service_cb);
  219. qmi_request_wait(qmi, &req.req);
  220. if (req.req.ret)
  221. return req.req.ret;
  222. client_id = req.cid;
  223. } else {
  224. qmi->service_keep_cid |= (1 << idx);
  225. }
  226. qmi->service_data[idx].connected = true;
  227. qmi->service_data[idx].client_id = client_id;
  228. qmi->service_data[idx].tid = 1;
  229. qmi->service_connected |= (1 << idx);
  230. return 0;
  231. }
  232. static void __qmi_service_disconnect(struct qmi_dev *qmi, int idx)
  233. {
  234. int client_id = qmi->service_data[idx].client_id;
  235. struct qmi_ctl_release_cid_request creq = {
  236. QMI_INIT_SEQUENCE(release_info,
  237. .service = qmi_services[idx],
  238. .cid = client_id,
  239. )
  240. };
  241. struct qmi_request req;
  242. struct qmi_msg *msg = qmi->buf;
  243. qmi->service_connected &= ~(1 << idx);
  244. qmi->service_data[idx].client_id = -1;
  245. qmi->service_data[idx].tid = 0;
  246. qmi_set_ctl_release_cid_request(msg, &creq);
  247. qmi_request_start(qmi, &req, NULL);
  248. qmi_request_wait(qmi, &req);
  249. }
  250. int qmi_service_release_client_id(struct qmi_dev *qmi, QmiService svc)
  251. {
  252. int idx = qmi_get_service_idx(svc);
  253. qmi->service_release_cid |= 1 << idx;
  254. return 0;
  255. }
  256. static void qmi_close_all_services(struct qmi_dev *qmi)
  257. {
  258. uint32_t connected = qmi->service_connected;
  259. int idx;
  260. qmi->service_keep_cid &= ~qmi->service_release_cid;
  261. for (idx = 0; connected; idx++, connected >>= 1) {
  262. if (!(connected & 1))
  263. continue;
  264. if (qmi->service_keep_cid & (1 << idx))
  265. continue;
  266. __qmi_service_disconnect(qmi, idx);
  267. }
  268. }
  269. int qmi_service_get_client_id(struct qmi_dev *qmi, QmiService svc)
  270. {
  271. int idx = qmi_get_service_idx(svc);
  272. if (idx < 0)
  273. return -1;
  274. qmi->service_keep_cid |= (1 << idx);
  275. return qmi->service_data[idx].client_id;
  276. }
  277. int qmi_device_open(struct qmi_dev *qmi, const char *path)
  278. {
  279. static struct {
  280. struct mbim_command_message mbim;
  281. union {
  282. char buf[2048];
  283. struct qmi_msg msg;
  284. } u;
  285. } __packed msgbuf;
  286. struct ustream *us = &qmi->sf.stream;
  287. int fd;
  288. uloop_init();
  289. fd = open(path, O_RDWR | O_EXCL | O_NONBLOCK | O_NOCTTY);
  290. if (fd < 0)
  291. return -1;
  292. us->notify_read = qmi_notify_read;
  293. ustream_fd_init(&qmi->sf, fd);
  294. INIT_LIST_HEAD(&qmi->req);
  295. qmi->ctl_tid = 1;
  296. qmi->buf = msgbuf.u.buf;
  297. return 0;
  298. }
  299. void qmi_device_close(struct qmi_dev *qmi)
  300. {
  301. struct qmi_request *req;
  302. qmi_close_all_services(qmi);
  303. ustream_free(&qmi->sf.stream);
  304. close(qmi->sf.fd.fd);
  305. while (!list_empty(&qmi->req)) {
  306. req = list_first_entry(&qmi->req, struct qmi_request, list);
  307. qmi_request_cancel(qmi, req);
  308. }
  309. }
  310. QmiService qmi_service_get_by_name(const char *str)
  311. {
  312. static const struct {
  313. const char *name;
  314. QmiService svc;
  315. } services[] = {
  316. { "dms", QMI_SERVICE_DMS },
  317. { "nas", QMI_SERVICE_NAS },
  318. { "pds", QMI_SERVICE_PDS },
  319. { "wds", QMI_SERVICE_WDS },
  320. { "wms", QMI_SERVICE_WMS },
  321. { "wda", QMI_SERVICE_WDA },
  322. { "uim", QMI_SERVICE_UIM },
  323. };
  324. int i;
  325. for (i = 0; i < ARRAY_SIZE(services); i++) {
  326. if (!strcasecmp(str, services[i].name))
  327. return services[i].svc;
  328. }
  329. return -1;
  330. }
  331. const char *qmi_get_error_str(int code)
  332. {
  333. int i;
  334. for (i = 0; i < ARRAY_SIZE(qmi_errors); i++) {
  335. if (qmi_errors[i].code == code)
  336. return qmi_errors[i].text;
  337. }
  338. return "Unknown error";
  339. }