1 /* 2 * uqmi -- tiny QMI support implementation 3 * 4 * Copyright (C) 2023 Alexander Couzens <lynxis@fe80.eu> 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 22 /* Used by uqmid to contain the modem state */ 23 24 #include <fcntl.h> 25 #include <unistd.h> 26 #include <stdlib.h> 27 #include <stdio.h> 28 #include <string.h> 29 #include <talloc.h> 30 31 #include "qmi-message.h" 32 33 #include "qmi-enums-dms.h" 34 #include "qmi-enums-wds.h" 35 #include "qmi-enums.h" 36 #include "sim_fsm.h" 37 #include "uqmid.h" 38 #include "qmi-errors.h" 39 #include "qmi-errors.c" 40 41 #include "mbim.h" 42 #include "services.h" 43 44 #include "modem.h" 45 #include "modem_fsm.h" 46 #include "modem_tx.h" 47 #include "osmocom/fsm.h" 48 #include "ubus.h" 49 #include "logging.h" 50 #include "utils.h" 51 52 LIST_HEAD(uqmid_modems); 53 54 struct modem * 55 uqmid_modem_find_by_device(const char *device) 56 { 57 struct modem *modem; 58 59 list_for_each_entry(modem, &uqmid_modems, list) { 60 if (!strcmp(device, modem->device)) 61 return modem; 62 } 63 64 return NULL; 65 } 66 67 struct modem * 68 uqmid_modem_find_by_name(const char *name) 69 { 70 struct modem *modem; 71 list_for_each_entry(modem, &uqmid_modems, list) { 72 if (!strcmp(name, modem->name)) 73 return modem; 74 } 75 76 return NULL; 77 } 78 79 static void 80 modem_error_cb(struct qmi_dev *dev, void *data) 81 { 82 struct modem *modem = data; 83 84 osmo_fsm_inst_term(modem->fi, OSMO_FSM_TERM_ERROR, modem); 85 uqmid_ubus_modem_destroy(modem); 86 qmi_device_close(modem->qmi, 0); 87 88 list_del(&modem->list); 89 talloc_free(modem); 90 } 91 92 /** 93 * 94 * @param name an unique name also used by ubus 95 * @param device path to the device. /dev/cdc-wdm0 96 * @param qmi_over_mbim true if qmi needs to be tunneled over mbim 97 * @return 0 on success 98 */ 99 int 100 uqmid_modem_add(const char *name, const char *device, bool qmi_over_mbim) 101 { 102 struct modem *modem = uqmid_modem_find_by_name(name); 103 int ret; 104 if (modem) 105 return -EALREADY; 106 107 modem = talloc_zero(NULL, struct modem); 108 modem->name = talloc_strdup(modem, name); 109 modem->device = talloc_strdup(modem, device); 110 modem->qmi = qmi_device_open(modem, device); 111 if (!modem->qmi) 112 goto err; 113 114 ret = uqmid_ubus_modem_add(modem); 115 if (ret) 116 goto err; 117 118 modem->qmi->error_cb = modem_error_cb; 119 modem->qmi->error_cb_data = modem; 120 modem->sim.use_uim = true; 121 122 modem->fi = modem_fsm_alloc(modem); 123 modem->sim.fi = sim_fsm_alloc(modem); 124 list_add(&modem->list, &uqmid_modems); 125 modem_fsm_start(modem); 126 127 return 0; 128 129 err: 130 if (modem->qmi) 131 qmi_device_close(modem->qmi, 0); 132 133 talloc_free(modem); 134 return -1; 135 } 136 137 138 static void 139 modem_remove_cb(struct qmi_dev *dev, void *data) 140 { 141 struct modem *modem = data; 142 143 osmo_fsm_inst_term(modem->fi, OSMO_FSM_TERM_REGULAR, modem); 144 uqmid_ubus_modem_destroy(modem); 145 146 list_del(&modem->list); 147 talloc_free(modem); 148 } 149 150 int 151 uqmid_modem_remove(struct modem *modem) 152 { 153 if (!modem) 154 return -EINVAL; 155 156 if (!modem->fi) 157 return 0; 158 159 if (modem->fi->state == MODEM_ST_DESTROY) 160 return 0; 161 162 if (modem->fi->proc.terminating) 163 return 0; 164 165 /* TODO: move the assignment of closing_cb */ 166 modem->qmi->closing_cb = modem_remove_cb; 167 modem->qmi->closing_cb_data = modem; 168 osmo_fsm_inst_dispatch(modem->fi, MODEM_EV_REQ_DESTROY, modem); 169 170 return 0; 171 } 172 173 int 174 uqmid_modem_configured(struct modem *modem) 175 { 176 osmo_fsm_inst_dispatch(modem->fi, MODEM_EV_REQ_CONFIGURED, NULL); 177 return 0; 178 } 179 180 static void 181 wds_get_packet_status_cb(struct qmi_service *service, struct qmi_request *req, struct qmi_msg *msg) 182 { 183 struct modem *modem = req->cb_data; 184 int ret = 0; 185 struct qmi_wds_get_packet_service_status_response res = {}; 186 187 if (req->ret) { 188 modem_log(modem, LOGL_INFO, "Failed to get operating mode. Status %d/%s.", req->ret, qmi_get_error_str(req->ret)); 189 /* FIXME: send ubus failed */ 190 return; 191 } 192 193 ret = qmi_parse_wds_get_packet_service_status_response(msg, &res); 194 if (ret) { 195 modem_log(modem, LOGL_INFO, "Failed to get packet service status. Failed to parse message"); 196 /* FIXME: send ubus failed */ 197 return; 198 } 199 200 if (!res.set.connection_status) { 201 modem_log(modem, LOGL_INFO, "Failed to get packet service status. No connection status"); 202 } 203 204 modem_log(modem, LOGL_INFO, "Conn Status is %d", res.data.connection_status); 205 206 /* Fix send back */ 207 return; 208 } 209 210 struct modem_opmode_data { 211 struct modem *modem; 212 void *cb_data; 213 uqmid_modem_get_opmode_cb cb; 214 }; 215 216 static void 217 modem_get_opmode_cb(struct qmi_service *service, struct qmi_request *req, struct qmi_msg *msg) 218 { 219 struct modem_opmode_data *data= req->cb_data; 220 struct modem *modem = data->modem; 221 int ret = 0; 222 struct qmi_dms_get_operating_mode_response res = {}; 223 224 if (!data) { 225 return; 226 } 227 228 if (req->ret) { 229 modem_log(modem, LOGL_INFO, "Failed to get operating mode. Status %d/%s.", req->ret, qmi_get_error_str(req->ret)); 230 goto out; 231 } 232 233 ret = qmi_parse_dms_get_operating_mode_response(msg, &res); 234 if (ret) { 235 modem_log(modem, LOGL_INFO, "Failed to get operating mode. Failed to parse message"); 236 goto out; 237 } 238 239 if (!res.set.mode) { 240 modem_log(modem, LOGL_INFO, "Failed to get operating mode. Failed to parse message"); 241 } 242 243 modem_log(modem, LOGL_INFO, "Opmode is %d", res.data.mode); 244 data->cb(data->cb_data, 0, res.data.mode); 245 out: 246 free(data); 247 return; 248 } 249 250 int 251 uqmid_modem_get_opmode(struct modem *modem, uqmid_modem_get_opmode_cb cb, void *cb_data) 252 { 253 struct qmi_service *dms = uqmi_service_find(modem->qmi, QMI_SERVICE_DMS); 254 if (!dms) { 255 cb(cb_data, -ENOENT, 0); 256 modem_log(modem, LOGL_ERROR, "Ubus call requested, but dms not available."); 257 return -1; 258 } 259 260 struct modem_opmode_data *data = calloc(1, sizeof(*data)); 261 if (!data) { 262 cb(cb_data, -ENOMEM, 0); 263 return -1; 264 } 265 266 data->cb = cb; 267 data->cb_data = cb_data; 268 data->modem = modem; 269 270 uqmi_service_send_simple(dms, qmi_set_dms_get_operating_mode_request, modem_get_opmode_cb, data); 271 return 0; 272 } 273 274 int uqmid_modem_networkstatus(struct modem *modem) 275 { 276 struct qmi_service *wds = uqmi_service_find(modem->qmi, QMI_SERVICE_WDS); 277 if (!wds) { 278 modem_log(modem, LOGL_ERROR, "Ubus call requested, but wds not available."); 279 return -1; 280 } 281 282 uqmi_service_send_simple(wds, qmi_set_wds_get_packet_service_status_request, wds_get_packet_status_cb, modem); 283 return 0; 284 } 285 286 void uqmid_modem_set_error(struct modem *modem, const char *error) 287 { 288 if (modem->state.error) { 289 talloc_free(modem->state.error); 290 } 291 292 if (error) { 293 modem_log(modem, LOGL_ERROR, "failed with %s", error); 294 modem->state.error = talloc_strdup(modem, error); 295 } else { 296 modem->state.error = NULL; 297 } 298 } 299
This page was automatically generated by LXR 0.3.1. • OpenWrt