static void registration_urc_pCREG_handler(struct ubmodem_s *modem, const struct at_cmd_def_s *cmd, const struct at_resp_info_s *info, const uint8_t *resp_stream, size_t stream_len, void *priv) { struct modem_sub_setup_network_s *sub = &modem->sub.setup_network; int8_t val; int ret; DEBUGASSERT(modem->state == MODEM_STATE_IN_SUBSTATE); /* * Response handler for +CREG URC. */ MODEM_DEBUGASSERT(modem, cmd == &urc_ATpCREG); if (info->status != RESP_STATUS_URC) { /* Should not happen. */ MODEM_DEBUGASSERT(modem, false); return; } if (!__ubmodem_stream_get_int8(&resp_stream, &stream_len, &val)) { /* Should not happen. */ MODEM_DEBUGASSERT(modem, false); return; } dbg("Network registration URC, +CREG=%d.\n", val); if (val >= 0 && val < ARRAY_SIZE(creg_strings)) { dbg("%s\n", creg_strings[val]); } else { dbg("Unknown +CREG status: %d\n", val); } if (sub->network_state < NETWORK_SETUP_WAITING_NETWORK_REGISTRATION) { /* Not ready yet, spurious +CREG URC? */ ubdbg("Ignored spurious +CREG URC.\n"); return; } switch (val) { case 1: case 5: /* * If 'val' is 1, registered: home network. * If 'val' is 5, registered: roamed. */ if (sub->network_state == NETWORK_SETUP_RETRYING_NETWORK_REGISTRATION) { /* Cannot do state transition to UBMODEM_LEVEL_NETWORK right now * as still retrying AT+COPS=0. Defer handling to * retry_ATpCOPS_handler. */ sub->received_creg_while_retrying = val; } else { network_registration_successed(modem, sub); } return; case 0: /* * MT not searching for connection. */ if (sub->network_state == NETWORK_SETUP_WAITING_NETWORK_REGISTRATION) { /* Stop timer before issuing command. */ if (modem->creg_timer_id >= 0) { __ubmodem_remove_timer(modem, modem->creg_timer_id); modem->creg_timer_id = -1; ubmodem_pm_set_activity(modem, UBMODEM_PM_ACTIVITY_HIGH, false); } /* Keep retrying automatic network registration until timeout. */ sub->network_state = NETWORK_SETUP_RETRYING_NETWORK_REGISTRATION; sub->received_creg_while_retrying = -1; ret = __ubmodem_set_timer(modem, 1000, &network_retry_registration_timer_handler, sub); MODEM_DEBUGASSERT(modem, ret != ERROR); modem->creg_timer_id = ret; ubmodem_pm_set_activity(modem, UBMODEM_PM_ACTIVITY_HIGH, true); } return; case 4: /* * If 'val' is 4, unknown registration error. * * Network registration flow-chart in "AT Commands Examples" * (UBX-13001820, R09, p.24) tells that proper action for this * event is to 'Wait'. */ return; case 3: /* * If 'val' is 3, registration was denied. * * Network registration flow-chart in "AT Commands Examples" * (UBX-13001820, R09, p.24) tells that proper action for this * event is 'DTE intervention may be required'. Further information * on event type '3': * * "3: the registration fails after a Location Update Reject; * possible causes are: * - Illegal MS * - Illegal ME * - IMSI unknown at HLR * - PLMN not allowed * - Location area not allowed * - Roaming not allowed in this location area * - Network failure * - Network congestion * * If the registration type is manual, then no further attempt is * made to search for a new PLMN or register with it. If the * registration type is automatic, the MS may look for an allowed * PLMN if the rejection cause was roaming restriction. In case of * illegal MS / ME, there could be possible problems with either * the SIM card or with the MT's identity (IMEI): user intervention * may be required." * * Modem can still be continuing connection effort after this event * especially in roaming conditions. Thus, we do not react on this * message but keep waiting for timeout or other indication of * network error. */ return; case 2: /* * If 'val' is 2, modem is trying to register. */ return; default: /* * Unknown. */ return; } }
static void monitor_urc_pCREG_handler(struct ubmodem_s *modem, const struct at_cmd_def_s *cmd, const struct at_resp_info_s *info, const uint8_t *resp_stream, size_t stream_len, void *priv) { int8_t val; int ret; (void)ret; /* * Response handler for +CREG URC. */ MODEM_DEBUGASSERT(modem, cmd == &urc_ATpCREG); if (info->status != RESP_STATUS_URC) { /* Should not happen. */ MODEM_DEBUGASSERT(modem, false); return; } if (!__ubmodem_stream_get_int8(&resp_stream, &stream_len, &val)) { /* Should not happen. */ MODEM_DEBUGASSERT(modem, false); return; } dbg("Network registration URC, +CREG=%d.\n", val); if (val >= 0 && val < ARRAY_SIZE(creg_strings)) { dbg("%s\n", creg_strings[val]); } else { dbg("Unknown +CREG status: %d\n", val); } if (modem->creg_timer_id >= 0) { __ubmodem_remove_timer(modem, modem->creg_timer_id); modem->creg_timer_id = -1; ubmodem_pm_set_activity(modem, UBMODEM_PM_ACTIVITY_HIGH, false); } switch (val) { default: /* Unknown? */ case 1: /* Switched to home network? */ case 5: /* Switched to roaming? */ return; case 2: /* Looking for connection. */ #ifdef CONFIG_UBMODEM_CREG_WAIT_NETWORK_TO_RESTORE /* Start connection search timeout timer. */ ret = __ubmodem_set_timer(modem, MODEM_CMD_NETWORK_TIMEOUT * 100, &network_reregister_timer_handler, modem); MODEM_DEBUGASSERT(modem, ret != ERROR); modem->creg_timer_id = ret; ubmodem_pm_set_activity(modem, UBMODEM_PM_ACTIVITY_HIGH, true); return; #else /* Reset network through SIM reinitialization. Appears to work * better in roaming conditions. TODO: Study why, do we understand * function of modem in low-signal environments properly? */ #endif case 3: /* Registration failed? */ case 4: /* Unknown registration error? */ case 0: /* MT not searching for connection. */ /* Unregister +CREG URC */ __ubparser_unregister_response_handler(&modem->parser, urc_ATpCREG.name); modem->creg_urc_registered = false; /* Issue new task. We cannot issue new state machine work from * URC 'context' as other work might be active in main state * machine. Task will be run with main state machine in proper * state. */ __ubmodem_add_task(modem, retry_network_through_sim, NULL); return; } }
static void urc_pCREG_handler(struct ubmodem_s *modem, const struct at_cmd_def_s *cmd, const struct at_resp_info_s *info, const uint8_t *resp_stream, size_t stream_len, void *priv) { int8_t val; int err; int ret; /* * Response handler for +CREG URC. */ MODEM_DEBUGASSERT(modem, cmd == &urc_ATpCREG); if (info->status != RESP_STATUS_URC) { /* Should not happen. */ MODEM_DEBUGASSERT(modem, false); return; } if (!__ubmodem_stream_get_int8(&resp_stream, &stream_len, &val)) { /* Should not happen. */ MODEM_DEBUGASSERT(modem, false); return; } dbg("Network registration URC, +CREG=%d.\n", val); if (val >= 0 && val < ARRAY_SIZE(creg_strings)) { dbg("%s\n", creg_strings[val]); } else { dbg("Unknown +CREG status: %d\n", val); } if (modem->level < UBMODEM_LEVEL_NETWORK) { struct modem_sub_setup_network_s *sub = &modem->sub.setup_network; DEBUGASSERT(modem->state == MODEM_STATE_IN_SUBSTATE); if (sub->network_state < NETWORK_SETUP_WAITING_NETWORK_REGISTRATION) { /* Not ready yet, spurious +CREG URC? */ ubdbg("Ignored spurious +CREG URC.\n"); return; } switch (val) { case 1: case 5: /* * If 'val' is 1, registered: home network. * If 'val' is 5, registered: roamed. */ if (sub->network_state == NETWORK_SETUP_RETRYING_NETWORK_REGISTRATION) { /* Cannot do state transition to UBMODEM_LEVEL_NETWORK right now * as still retrying AT+COPS=0. Defer handling to * retry_ATpCOPS_handler. */ sub->received_creg_while_retrying = val; } else { /* Report successful cellular network connection. */ sub->keep_creg_urc = true; __ubmodem_reached_level(modem, UBMODEM_LEVEL_NETWORK); } return; case 0: /* * MT not searching for connection. */ if (sub->network_state == NETWORK_SETUP_WAITING_NETWORK_REGISTRATION) { /* Keep retrying automatic network registration until timeout. */ sub->network_state = NETWORK_SETUP_RETRYING_NETWORK_REGISTRATION; sub->received_creg_while_retrying = -1; err = __ubmodem_send_cmd(modem, &cmd_ATpCOPS, retry_ATpCOPS_handler, sub, "%s", "=0"); MODEM_DEBUGASSERT(modem, err == OK); } return; case 4: /* * If 'val' is 4, unknown registration error. */ /* no break */ case 3: /* * If 'val' is 3, registration was denied. */ /* Perform fail-over code from timeout handler. */ network_register_timer_handler(modem, -1, sub); return; case 2: /* * If 'val' is 2, modem is trying to register. */ return; default: /* * Unknown. */ return; } } else { /* Current level is UBMODEM_LEVEL_NETWORK or higher. */ switch (val) { default: /* Unknown? */ case 1: /* Switched to home network? */ case 5: /* Switched to roaming? */ if (modem->creg_timer_id >= 0) { __ubmodem_remove_timer(modem, modem->creg_timer_id); modem->creg_timer_id = -1; } return; case 2: /* Looking for connection. */ /* Start connection search timeout timer. */ ret = __ubmodem_set_timer(modem, MODEM_CMD_NETWORK_TIMEOUT * 100, &network_reregister_timer_handler, modem); if (ret == ERROR) { /* Error here? Add assert? Or just try bailout? */ MODEM_DEBUGASSERT(modem, false); (void)network_reregister_timer_handler(modem, -1, modem); return; } modem->creg_timer_id = ret; return; case 3: /* Registration failed? */ case 4: /* Unknown registration error? */ case 0: /* MT not searching for connection. */ /* Unregister +CREG URC */ __ubparser_unregister_response_handler(&modem->parser, urc_ATpCREG.name); modem->creg_urc_registered = false; /* Issue new task. We cannot issue new state machine work from * URC 'context' as other work might be active in main state * machine. Task will be run with main state machine in proper * state. */ __ubmodem_add_task(modem, retry_network_through_sim, NULL); return; } } }
static void urc_voice_clip_handler(struct ubmodem_s *modem, const struct at_cmd_def_s *cmd, const struct at_resp_info_s *info, const uint8_t *resp_stream, size_t stream_len, void *priv) { const char *number; uint16_t number_len; int16_t number_type; const char *subaddr; uint16_t sa_len; int16_t sa_type; const char *alpha; uint16_t alpha_len; int8_t cli_valid; const char *cli_valid_str; /* * URC handler for 'data available for sockets on modem' */ MODEM_DEBUGASSERT(modem, cmd == &urc_ATpCLIP); MODEM_DEBUGASSERT(modem, info->status == RESP_STATUS_URC); /* Get number. */ if (!__ubmodem_stream_get_string(&resp_stream, &stream_len, &number, &number_len)) { MODEM_DEBUGASSERT(modem, false); /* Should not get here. */ return; } /* Get number type. */ if (!__ubmodem_stream_get_int16(&resp_stream, &stream_len, &number_type)) { MODEM_DEBUGASSERT(modem, false); /* Should not get here. */ return; } /* Get sub-address. */ if (!__ubmodem_stream_get_string(&resp_stream, &stream_len, &subaddr, &sa_len)) { MODEM_DEBUGASSERT(modem, false); /* Should not get here. */ return; } /* Get sub-address type. */ if (!__ubmodem_stream_get_int16(&resp_stream, &stream_len, &sa_type)) { MODEM_DEBUGASSERT(modem, false); /* Should not get here. */ return; } /* Get alpha. */ if (!__ubmodem_stream_get_string(&resp_stream, &stream_len, &alpha, &alpha_len)) { MODEM_DEBUGASSERT(modem, false); /* Should not get here. */ return; } /* Get CLI validity. */ if (!__ubmodem_stream_get_int8(&resp_stream, &stream_len, &cli_valid)) { MODEM_DEBUGASSERT(modem, false); /* Should not get here. */ return; } /* Report. */ switch (cli_valid) { case MODEM_CLI_VALID: cli_valid_str = "valid"; break; case MODEM_CLI_WITHHELD: cli_valid_str = "withheld"; break; case MODEM_CLI_NOT_AVAILABLE: cli_valid_str = "not available"; break; default: cli_valid_str = NULL; break; } dbg("+CLIP: CLI %s: number='%s', type=%d, sa='%s', satype=%d, alpha='%s'.\n", cli_valid_str, number, number_type, subaddr, sa_type, alpha); /* Update CLIP state. */ if (cli_valid_str == NULL) cli_valid = MODEM_CLI_NOT_AVAILABLE; modem_voice_new_clip(modem, cli_valid == MODEM_CLI_VALID, number, number_type, subaddr, sa_type); }
static void urc_voice_ucallstat_handler(struct ubmodem_s *modem, const struct at_cmd_def_s *cmd, const struct at_resp_info_s *info, const uint8_t *resp_stream, size_t stream_len, void *priv) { int8_t stat; int32_t call_id; const char *stat_str; /* * URC handler for 'data available for sockets on modem' */ MODEM_DEBUGASSERT(modem, cmd == &urc_ATpUCALLSTAT); MODEM_DEBUGASSERT(modem, info->status == RESP_STATUS_URC); /* Get call_id. */ if (!__ubmodem_stream_get_int32(&resp_stream, &stream_len, &call_id)) { MODEM_DEBUGASSERT(modem, false); /* Should not get here. */ return; } /* Get status. */ if (!__ubmodem_stream_get_int8(&resp_stream, &stream_len, &stat)) { MODEM_DEBUGASSERT(modem, false); /* Should not get here. */ return; } /* Report. */ switch (stat) { case MODEM_UCALLSTAT_ACTIVE: stat_str = "active"; break; case MODEM_UCALLSTAT_HOLD: stat_str = "hold"; break; case MODEM_UCALLSTAT_DIALING: stat_str = "dialing (MO)"; break; case MODEM_UCALLSTAT_ALERTING: stat_str = "alerting (MO)"; break; case MODEM_UCALLSTAT_RINGING: stat_str = "ringing (MT)"; break; case MODEM_UCALLSTAT_WAITING: stat_str = "waiting (MT)"; break; case MODEM_UCALLSTAT_DISCONNECTED: stat_str = "disconnected"; break; case MODEM_UCALLSTAT_CONNECTED: stat_str = "connected"; break; default: stat_str = NULL; break; } dbg("+UCALLSTAT: call_id=%d, status: %s (%d).\n", call_id, stat_str, stat); /* Update CLIP state. */ if (stat_str == NULL) stat = MODEM_UCALLSTAT_DISCONNECTED; modem_voice_update_ucallstat(modem, stat); }
static void socket_recvfrom_handler(struct ubmodem_s *modem, const struct at_cmd_def_s *cmd, const struct at_resp_info_s *info, const uint8_t *resp_stream, size_t stream_len, void *priv) { struct usrsock_message_datareq_ack_s resp = {}; struct modem_socket_s *sock = priv; uint16_t buflen; int8_t sockid; const char *buf; size_t wlen; struct sockaddr_in addr = {}; /* * Response handler for +USORF sockets data read. */ MODEM_DEBUGASSERT(modem, cmd == ((sock->type == SOCK_DGRAM) ? &cmd_ATpUSORF : &cmd_ATpUSORD)); if (sock->is_closed) { /* Socket has been closed, report error. */ (void)__ubmodem_usrsock_send_response(modem, &sock->req, false, -EPIPE); __ubsocket_work_done(sock); return; } if (info->status == RESP_STATUS_ERROR) { /* Not CME error, fetch sockets error. */ __ubmodem_get_socket_error(sock); return; } if (resp_status_is_error_or_timeout(info->status) || info->status != RESP_STATUS_OK) { dbg("%s error, status: %d\n", cmd->name, info->status); /* Reading failed? This should not happen as we read only if modem * reported that there is data to be read. */ (void)__ubmodem_usrsock_send_response(modem, &sock->req, false, -EPIPE); __ubsocket_work_done(sock); return; } /* Read the sockets number. */ if (!__ubmodem_stream_get_int8(&resp_stream, &stream_len, &sockid)) { /* Invalid data from modem? Might be missing character from "+USORF:" * header. */ dbg("Invalid %s response, %s, stream_len: %d\n", cmd->name, "no sockid", stream_len); (void)__ubmodem_usrsock_send_response(modem, &sock->req, false, -EPIPE); __ubsocket_work_done(sock); return; } if (sockid != sock->modem_sd) { /* Some other error, report generic error. */ dbg("Invalid %s response, sockid mismatch, got: %d, expect: %d\n", cmd->name, sockid, sock->modem_sd); (void)__ubmodem_usrsock_send_response(modem, &sock->req, false, -EPIPE); __ubsocket_work_done(sock); return; } if (sock->type == SOCK_DGRAM) { int32_t port; const char *addrstr; uint16_t addrstrlen; /* Read address */ if (!__ubmodem_stream_get_string(&resp_stream, &stream_len, &addrstr, &addrstrlen)) { dbg("Invalid %s response, %s, stream_len: %d\n", cmd->name, "no address", stream_len); (void)__ubmodem_usrsock_send_response(modem, &sock->req, false, -EPIPE); __ubsocket_work_done(sock); return; } /* Read port */ if (!__ubmodem_stream_get_int32(&resp_stream, &stream_len, &port)) { dbg("Invalid %s response, %s, stream_len: %d\n", cmd->name, "no port", stream_len); (void)__ubmodem_usrsock_send_response(modem, &sock->req, false, -EPIPE); __ubsocket_work_done(sock); return; } /* Prepare peer address for response */ addr.sin_addr.s_addr = inet_addr(addrstr); addr.sin_port = htons(port); addr.sin_family = AF_INET; } else { /* Prepare peer address for response */ addr = sock->connect.peeraddr; } /* Read data buffer */ if (!__ubmodem_stream_get_string(&resp_stream, &stream_len, &buf, &buflen)) { dbg("Invalid %s response, %s, stream_len: %d\n", cmd->name, "no buflen", stream_len); (void)__ubmodem_usrsock_send_response(modem, &sock->req, false, -EPIPE); __ubsocket_work_done(sock); return; } /* Stream now has data, feed data into usrsock link. */ resp.reqack.xid = sock->req.xid; resp.reqack.head.msgid = USRSOCK_MESSAGE_RESPONSE_DATA_ACK; resp.reqack.head.flags = 0; resp.reqack.result = buflen; resp.valuelen_nontrunc = sizeof(struct sockaddr_in); resp.valuelen = resp.valuelen_nontrunc; if (resp.valuelen > sock->recv.max_addrlen) { resp.valuelen = sock->recv.max_addrlen; } /* Give debug trace if active. */ if (modem->active_events & UBMODEM_EVENT_FLAG_TRACE_USRSOCK) { int tmp[4] = { UBMODEM_TRACE_USRSOCK_DATARESP, resp.reqack.xid, resp.reqack.result, resp.valuelen }; __ubmodem_publish_event(modem, UBMODEM_EVENT_FLAG_TRACE_USRSOCK, tmp, sizeof(tmp)); } /* Send response header. */ wlen = write(modem->sockets.usrsockfd, &resp, sizeof(resp)); MODEM_DEBUGASSERT(modem, wlen == sizeof(resp)); if (resp.valuelen > 0) { /* Send address. */ wlen = write(modem->sockets.usrsockfd, &addr, resp.valuelen); MODEM_DEBUGASSERT(modem, wlen == resp.valuelen); } /* Send data. */ wlen = write(modem->sockets.usrsockfd, buf, resp.reqack.result); MODEM_DEBUGASSERT(modem, wlen == resp.reqack.result); /* Done reading. */ __ubsocket_work_done(sock); }