qmimodem: change msg class none to store/notify

Change message class none configuration from directly delivering a new
message in the event indication to storing it in the modem's NV then
sending an event indication identifying the new message. Also, add
checking for stored, unread messages at boot up.

This fixes a scenario where the modem powers on, automatically
connects to the network, receives a message, and sends an indication
before oFono has fully initialized. Now, the message is saved and
retrieved after initialization.

This also helps with suspend/resume on platforms where the modem,
supports waking the host CPU upon SMS reception. And, the platform
design limitation is that the modem's interfaces (eg, cdc-wdm, wwan,
etc) disconnect on suspend and reconnect on resume. This causes oFono
to re-initialize and miss the message indication because it was sent
before oFono is ready. Now, the wake up message is saved and retrieved
after initialization.
This commit is contained in:
Tom Nguyen 2019-06-05 12:26:23 -04:00 committed by Denis Kenzior
parent c1ccae7efc
commit df36faccd9
2 changed files with 378 additions and 69 deletions

View File

@ -39,8 +39,17 @@ struct sms_data {
struct qmi_service *wms;
uint16_t major;
uint16_t minor;
struct qmi_wms_read_msg_id rd_msg_id;
struct qmi_wms_result_msg_list *msg_list;
uint32_t rd_msg_num;
uint8_t msg_mode;
bool msg_mode_all;
bool msg_list_chk;
};
static void get_msg_list(struct ofono_sms *sms);
static void raw_read(struct ofono_sms *sms, uint8_t type, uint32_t ndx);
static void get_smsc_addr_cb(struct qmi_result *result, void *user_data)
{
struct cb_data *cbd = user_data;
@ -334,21 +343,95 @@ error:
g_free(cbd);
}
static void delete_msg_cb(struct qmi_result *result, void *user_data)
{
struct ofono_sms *sms = user_data;
struct sms_data *data = ofono_sms_get_data(sms);
uint16_t err;
DBG("");
if (qmi_result_set_error(result, &err))
DBG("Err: delete %d - %s", err, qmi_result_get_error(result));
/*
* Continue processing msg list. If error occurred, something
* serious happened, then don't bother.
*/
if (data->msg_list && data->msg_list_chk) {
uint32_t msg = ++data->rd_msg_num;
/*
* Get another msg. If list is empty check for more. Once query
* returns empty, rely on event indication to get new msgs.
*/
if (msg < data->msg_list->cnt)
raw_read(sms, data->msg_list->msg[msg].type,
GUINT32_FROM_LE(data->msg_list->msg[msg].ndx));
else
get_msg_list(sms);
}
}
static void delete_msg(struct ofono_sms *sms, uint8_t tag)
{
struct sms_data *data = ofono_sms_get_data(sms);
struct qmi_param *param;
qmi_result_func_t func = NULL;
DBG("");
param = qmi_param_new();
if (param == NULL)
goto done;
qmi_param_append_uint8(param, QMI_WMS_PARAM_DEL_STORE,
QMI_WMS_STORAGE_TYPE_NV);
if (tag == QMI_WMS_MT_UNDEFINE) {
DBG("delete read msg type %d ndx %d", data->rd_msg_id.type,
data->rd_msg_id.ndx);
/* delete 1 msg */
qmi_param_append_uint32(param, QMI_WMS_PARAM_DEL_NDX,
data->rd_msg_id.ndx);
func = delete_msg_cb;
} else {
DBG("delete msg tag %d mode %d", tag, data->msg_mode);
/* delete all msgs from 1 tag type */
qmi_param_append_uint8(param, QMI_WMS_PARAM_DEL_TYPE, tag);
}
qmi_param_append_uint8(param, QMI_WMS_PARAM_DEL_MODE, data->msg_mode);
if (qmi_service_send(data->wms, QMI_WMS_DELETE, param,
func, sms, NULL) > 0)
return;
qmi_param_free(param);
done:
data->msg_list_chk = false;
}
static void raw_read_cb(struct qmi_result *result, void *user_data)
{
struct ofono_sms *sms = user_data;
const struct qmi_wms_raw_message* msg;
uint16_t len;
uint16_t error;
struct sms_data *data = ofono_sms_get_data(sms);
const struct qmi_wms_raw_message *msg;
uint16_t err;
if (qmi_result_set_error(result, &error)) {
DBG("Raw read error: %d (%s)", error,
qmi_result_get_error(result));
DBG("");
if (qmi_result_set_error(result, &err)) {
DBG("Err: read %d - %s", err, qmi_result_get_error(result));
data->msg_list_chk = false;
return;
}
/* Raw message data */
msg = qmi_result_get(result, 0x01, &len);
msg = qmi_result_get(result, QMI_WMS_RESULT_READ_MSG, NULL);
if (msg) {
uint16_t plen;
uint16_t tpdu_len;
@ -357,9 +440,174 @@ static void raw_read_cb(struct qmi_result *result, void *user_data)
tpdu_len = plen - msg->msg_data[0] - 1;
ofono_sms_deliver_notify(sms, msg->msg_data, plen, tpdu_len);
} else {
DBG("No message data available at requested position");
} else
DBG("Err: no data in type %d ndx %d", data->rd_msg_id.type,
data->rd_msg_id.ndx);
/* delete read msg */
delete_msg(sms, QMI_WMS_MT_UNDEFINE);
}
static void raw_read(struct ofono_sms *sms, uint8_t type, uint32_t ndx)
{
struct sms_data *data = ofono_sms_get_data(sms);
struct qmi_param *param;
DBG("");
param = qmi_param_new();
if (param == NULL)
goto done;
data->rd_msg_id.type = type;
data->rd_msg_id.ndx = ndx;
DBG("read type %d ndx %d", data->rd_msg_id.type, data->rd_msg_id.ndx);
qmi_param_append(param, QMI_WMS_PARAM_READ_MSG,
sizeof(data->rd_msg_id), &data->rd_msg_id);
qmi_param_append_uint8(param, QMI_WMS_PARAM_READ_MODE, data->msg_mode);
if (qmi_service_send(data->wms, QMI_WMS_RAW_READ, param,
raw_read_cb, sms, NULL) > 0)
return;
qmi_param_free(param);
done:
data->msg_list_chk = false;
}
static void get_msg_list_cb(struct qmi_result *result, void *user_data)
{
struct ofono_sms *sms = user_data;
struct sms_data *data = ofono_sms_get_data(sms);
const struct qmi_wms_result_msg_list *list;
uint32_t cnt = 0;
uint16_t tmp;
DBG("");
if (qmi_result_set_error(result, &tmp)) {
DBG("Err: get msg list mode=%d %d=%s", data->msg_mode, tmp,
qmi_result_get_error(result));
goto done;
}
list = qmi_result_get(result, QMI_WMS_RESULT_MSG_LIST, NULL);
if (list == NULL) {
DBG("Err: get msg list empty");
goto done;
}
cnt = GUINT32_FROM_LE(list->cnt);
DBG("msgs found %d", cnt);
for (tmp = 0; tmp < cnt; tmp++) {
DBG("unread type %d ndx %d", list->msg[tmp].type,
GUINT32_FROM_LE(list->msg[tmp].ndx));
}
/* free list from last time */
if (data->msg_list) {
g_free(data->msg_list);
data->msg_list = NULL;
}
/* save list and get 1st msg */
if (cnt) {
int msg_size = cnt * sizeof(list->msg[0]);
data->msg_list = g_try_malloc0(sizeof(list->cnt) + msg_size);
if (data->msg_list == NULL)
goto done;
data->msg_list->cnt = cnt;
memcpy(data->msg_list->msg, list->msg, msg_size);
data->rd_msg_num = 0;
raw_read(sms, data->msg_list->msg[0].type,
GUINT32_FROM_LE(data->msg_list->msg[0].ndx));
return;
}
done:
data->msg_list_chk = false;
/* if both protocols supported, check the other */
if (data->msg_mode_all) {
data->msg_mode_all = false;
data->msg_mode = QMI_WMS_MESSAGE_MODE_GSMWCDMA;
get_msg_list(sms);
}
}
static void get_msg_list(struct ofono_sms *sms)
{
struct sms_data *data = ofono_sms_get_data(sms);
struct qmi_param *param;
DBG("");
param = qmi_param_new();
if (param == NULL)
return;
data->msg_list_chk = true;
/* query NOT_READ msg list */
qmi_param_append_uint8(param, QMI_WMS_PARAM_STORAGE_TYPE,
QMI_WMS_STORAGE_TYPE_NV);
qmi_param_append_uint8(param, QMI_WMS_PARAM_TAG_TYPE,
QMI_WMS_MT_NOT_READ);
qmi_param_append_uint8(param, QMI_WMS_PARAM_MESSAGE_MODE,
data->msg_mode);
if (qmi_service_send(data->wms, QMI_WMS_GET_MSG_LIST, param,
get_msg_list_cb, sms, NULL) > 0)
return;
data->msg_list_chk = false;
qmi_param_free(param);
}
static void get_msg_protocol_cb(struct qmi_result *result, void *user_data)
{
struct ofono_sms *sms = user_data;
struct sms_data *data = ofono_sms_get_data(sms);
uint16_t err;
DBG("");
if (qmi_result_set_error(result, &err) &&
(err != QMI_ERR_OP_DEVICE_UNSUPPORTED)) {
DBG("Err: protocol %d - %s", err, qmi_result_get_error(result));
return;
}
if (err != QMI_ERR_OP_DEVICE_UNSUPPORTED) {
/* modem supports only 1 protocol */
qmi_result_get_uint8(result, QMI_WMS_PARAM_PROTOCOL,
&data->msg_mode);
} else {
/* check both, start with 1 then switch to other */
DBG("device supports CDMA and WCDMA msg protocol");
data->msg_mode_all = true;
data->msg_mode = QMI_WMS_MESSAGE_MODE_CDMA;
}
/* check for messages */
get_msg_list(sms);
}
static void get_msg_protocol(struct ofono_sms *sms)
{
struct sms_data *data = ofono_sms_get_data(sms);
DBG("");
qmi_service_send(data->wms, QMI_WMS_GET_MSG_PROTOCOL, NULL,
get_msg_protocol_cb, sms, NULL);
}
static void event_notify(struct qmi_result *result, void *user_data)
@ -367,66 +615,78 @@ static void event_notify(struct qmi_result *result, void *user_data)
struct ofono_sms *sms = user_data;
struct sms_data *data = ofono_sms_get_data(sms);
const struct qmi_wms_result_new_msg_notify *notify;
const struct qmi_wms_result_message *message;
uint16_t len;
DBG("");
notify = qmi_result_get(result, QMI_WMS_RESULT_NEW_MSG_NOTIFY, &len);
/*
* The 2 types of MT message TLVs are mutually exclusive, depending on
* how the route action is configured. If action is store and notify,
* then the MT message TLV is sent. If action is transfer only or
* transfer and ack, then the transfer route MT message TLV is sent.
*/
notify = qmi_result_get(result, QMI_WMS_RESULT_NEW_MSG_NOTIFY, NULL);
if (notify) {
DBG("storage type %d index %d", notify->storage_type,
GUINT32_FROM_LE(notify->storage_index));
}
/* route is store and notify */
if (!qmi_result_get_uint8(result, QMI_WMS_RESULT_MSG_MODE,
&data->msg_mode))
DBG("msg mode not found, use mode %d", data->msg_mode);
message = qmi_result_get(result, QMI_WMS_RESULT_MESSAGE, &len);
if (message) {
uint16_t plen;
DBG("msg type %d ndx %d mode %d", notify->storage_type,
GUINT32_FROM_LE(notify->storage_index), data->msg_mode);
plen = GUINT16_FROM_LE(message->msg_length);
DBG("ack_required %d transaction id %u", message->ack_required,
GUINT32_FROM_LE(message->transaction_id));
DBG("msg format %d PDU length %d", message->msg_format, plen);
ofono_sms_deliver_notify(sms, message->msg_data, plen, plen);
/* don't read if list is being processed, get this msg later */
if (!data->msg_list_chk)
raw_read(sms, notify->storage_type,
GUINT32_FROM_LE(notify->storage_index));
} else {
/* The Quectel EC21, at least, does not provide the
* message data in the event notification, so a 'raw read'
* needs to be issued in order to query the message itself
*/
struct qmi_param *param;
/* route is either transfer only or transfer and ACK */
const struct qmi_wms_result_message *message;
param = qmi_param_new();
if (!param)
return;
message = qmi_result_get(result, QMI_WMS_RESULT_MESSAGE, NULL);
if (message) {
uint16_t plen;
/* Message memory storage ID */
qmi_param_append(param, 0x01, sizeof(*notify), notify);
/* The 'message mode' parameter is documented as optional,
* but the Quectel EC21 errors out with error 17 (missing
* argument) if it is not provided... we default to 3GPP
* here because that's what works for me and it's not clear
* how to actually query what this should be otherwise...
*/
/* Message mode */
qmi_param_append_uint8(param, 0x10,
QMI_WMS_MESSAGE_MODE_GSMWCDMA);
plen = GUINT16_FROM_LE(message->msg_length);
if (qmi_service_send(data->wms, QMI_WMS_RAW_READ, param,
raw_read_cb, sms, NULL) > 0)
return;
DBG("ack_required %d transaction id %u",
message->ack_required,
GUINT32_FROM_LE(message->transaction_id));
DBG("msg format %d PDU length %d",
message->msg_format, plen);
qmi_param_free(param);
ofono_sms_deliver_notify(sms, message->msg_data,
plen, plen);
}
}
}
static void set_routes_cb(struct qmi_result *result, void *user_data)
{
struct ofono_sms *sms = user_data;
struct sms_data *data = ofono_sms_get_data(sms);
DBG("");
ofono_sms_register(sms);
/*
* Modem storage is limited. As a fail safe, delete processed messages
* to free device memory to prevent blockage of new messages.
*/
data->msg_mode = QMI_WMS_MESSAGE_MODE_CDMA;
delete_msg(sms, QMI_WMS_MT_READ);
delete_msg(sms, QMI_WMS_MO_SENT);
data->msg_mode = QMI_WMS_MESSAGE_MODE_GSMWCDMA;
delete_msg(sms, QMI_WMS_MT_READ);
delete_msg(sms, QMI_WMS_MO_SENT);
/*
* Subsystem initialized, now start process to check for unread
* messages. First, query msg protocol/mode. If modem supports both
* modes, then check messages for both modes since there's no way to
* query which mode is active.
*/
get_msg_protocol(sms);
}
static void get_routes_cb(struct qmi_result *result, void *user_data)
@ -468,8 +728,8 @@ static void get_routes_cb(struct qmi_result *result, void *user_data)
new_list->count = GUINT16_TO_LE(1);
new_list->route[0].msg_type = QMI_WMS_MSG_TYPE_P2P;
new_list->route[0].msg_class = QMI_WMS_MSG_CLASS_NONE;
new_list->route[0].storage_type = QMI_WMS_STORAGE_TYPE_NONE;
new_list->route[0].action = QMI_WMS_ACTION_TRANSFER_AND_ACK;
new_list->route[0].storage_type = QMI_WMS_STORAGE_TYPE_NV;
new_list->route[0].action = QMI_WMS_ACTION_STORE_AND_NOTIFY;
param = qmi_param_new();
if (!param)
@ -524,6 +784,9 @@ static void create_wms_cb(struct qmi_service *service, void *user_data)
data->wms = qmi_service_ref(service);
memset(&data->rd_msg_id, 0, sizeof(data->rd_msg_id));
data->msg_mode = QMI_WMS_MESSAGE_MODE_GSMWCDMA;
qmi_service_register(data->wms, QMI_WMS_EVENT,
event_notify, sms, NULL);
@ -568,6 +831,9 @@ static void qmi_sms_remove(struct ofono_sms *sms)
qmi_service_unref(data->wms);
if (data->msg_list)
g_free(data->msg_list);
g_free(data);
}

View File

@ -25,8 +25,9 @@
#define QMI_WMS_RAW_SEND 32 /* Send a raw message */
#define QMI_WMS_RAW_READ 34 /* Read raw message from storage*/
#define QMI_WMS_RAW_READ 34 /* Read raw message from storage */
#define QMI_WMS_DELETE 36 /* Delete message */
#define QMI_WMS_GET_MSG_PROTOCOL 48 /* Get message protocol */
#define QMI_WMS_GET_MSG_LIST 49 /* Get list of messages from the device */
#define QMI_WMS_SET_ROUTES 50 /* Set routes for message memory storage */
#define QMI_WMS_GET_ROUTES 51 /* Get routes for message memory storage */
@ -45,6 +46,17 @@ struct qmi_wms_result_new_msg_notify {
uint32_t storage_index;
} __attribute__((__packed__));
#define QMI_WMS_RESULT_MESSAGE 0x11
struct qmi_wms_result_message {
uint8_t ack_required; /* bool */
uint32_t transaction_id;
uint8_t msg_format;
uint16_t msg_length;
uint8_t msg_data[0];
} __attribute__((__packed__));
#define QMI_WMS_RESULT_MSG_MODE 0x12
/* Set new message conditions */
#define QMI_WMS_PARAM_NEW_MSG_REPORT 0x10 /* bool */
@ -57,17 +69,16 @@ struct qmi_wms_param_message {
} __attribute__((__packed__));
#define QMI_WMS_RESULT_MESSAGE_ID 0x01 /* uint16 */
/* Get list of messages from the device */
#define QMI_WMS_PARAM_STORAGE_TYPE 0x01 /* uint8 */
#define QMI_WMS_PARAM_MESSAGE_MODE 0x11 /* uint8 */
/* Read a raw message */
#define QMI_WMS_PARAM_READ_MSG 0x01
struct qmi_wms_read_msg_id {
uint8_t type;
uint32_t ndx;
} __attribute__((__packed__));
#define QMI_WMS_STORAGE_TYPE_UIM 0
#define QMI_WMS_STORAGE_TYPE_NV 1
#define QMI_WMS_STORAGE_TYPE_UNKNOWN 2
#define QMI_WMS_STORAGE_TYPE_NONE 255
#define QMI_WMS_MESSAGE_MODE_GSMWCDMA 1
#define QMI_WMS_PARAM_READ_MODE 0x10
#define QMI_WMS_RESULT_READ_MSG 0x01
struct qmi_wms_raw_message {
uint8_t msg_tag;
uint8_t msg_format;
@ -75,6 +86,43 @@ struct qmi_wms_raw_message {
uint8_t msg_data[0];
} __attribute__((__packed__));
/* Delete messages */
#define QMI_WMS_PARAM_DEL_STORE 0x01
#define QMI_WMS_PARAM_DEL_NDX 0x10
#define QMI_WMS_PARAM_DEL_TYPE 0x11
#define QMI_WMS_PARAM_DEL_MODE 0x12
/* Get message protocol */
#define QMI_WMS_PARAM_PROTOCOL 0x01
/* Get list of messages from the device */
#define QMI_WMS_PARAM_STORAGE_TYPE 0x01 /* uint8 */
#define QMI_WMS_PARAM_TAG_TYPE 0x10
#define QMI_WMS_PARAM_MESSAGE_MODE 0x11 /* uint8 */
#define QMI_WMS_RESULT_MSG_LIST 0x01
struct qmi_wms_result_msg_list {
uint32_t cnt;
struct {
uint32_t ndx;
uint8_t type;
} __attribute__((__packed__)) msg[0];
} __attribute__((__packed__));
#define QMI_WMS_STORAGE_TYPE_UIM 0
#define QMI_WMS_STORAGE_TYPE_NV 1
#define QMI_WMS_STORAGE_TYPE_UNKNOWN 2
#define QMI_WMS_STORAGE_TYPE_NONE 255
#define QMI_WMS_MT_READ 0x00
#define QMI_WMS_MT_NOT_READ 0x01
#define QMI_WMS_MO_SENT 0x02
#define QMI_WMS_MO_NOT_SENT 0x03
#define QMI_WMS_MT_UNDEFINE 0xff
#define QMI_WMS_MESSAGE_MODE_CDMA 0x00
#define QMI_WMS_MESSAGE_MODE_GSMWCDMA 0x01
/* Get routes for message memory storage */
#define QMI_WMS_RESULT_ROUTE_LIST 0x01
#define QMI_WMS_PARAM_ROUTE_LIST 0x01
@ -89,14 +137,6 @@ struct qmi_wms_route_list {
} __attribute__((__packed__));
#define QMI_WMS_RESULT_STATUS_REPORT 0x10 /* bool */
#define QMI_WMS_PARAM_STATUS_REPORT 0x10 /* bool */
#define QMI_WMS_RESULT_MESSAGE 0x11
struct qmi_wms_result_message {
uint8_t ack_required; /* bool */
uint32_t transaction_id;
uint8_t msg_format;
uint16_t msg_length;
uint8_t msg_data[0];
} __attribute__((__packed__));
#define QMI_WMS_MSG_TYPE_P2P 0x00
#define QMI_WMS_MSG_TYPE_BROADCAST 0x01
@ -134,3 +174,6 @@ struct qmi_wms_result_smsc_addr {
#define QMI_WMS_DOMAIN_PS_PREFERRED 0x01
#define QMI_WMS_DOMAIN_CS_ONLY 0x02
#define QMI_WMS_DOMAIN_PS_ONLY 0x03
/* Error code */
#define QMI_ERR_OP_DEVICE_UNSUPPORTED 0x19