From: Yan Wang yan.wang@linux.intel.com
Host message queue is long sometimes. So when one new IPC message like DMA trace host offset is pushed into, the previous same type IPC message may haven't been sent. For every type of IPC message, there are different comparison conditions. So need a group of sub-finding functions to do and user also can extend them easily based on new requirements in the future.
Signed-off-by: Yan Wang yan.wang@linux.intel.com --- src/include/reef/ipc.h | 2 +- src/ipc/intel-ipc.c | 128 ++++++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 118 insertions(+), 12 deletions(-)
diff --git a/src/include/reef/ipc.h b/src/include/reef/ipc.h index fcab45b..bb814be 100644 --- a/src/include/reef/ipc.h +++ b/src/include/reef/ipc.h @@ -125,7 +125,7 @@ int ipc_stream_send_xrun(struct comp_dev *cdev,
int ipc_queue_host_message(struct ipc *ipc, uint32_t header, void *tx_data, size_t tx_bytes, void *rx_data, - size_t rx_bytes, void (*cb)(void*, void*), void *cb_data); + size_t rx_bytes, void (*cb)(void*, void*), void *cb_data, uint32_t replace); int ipc_send_short_msg(uint32_t msg);
void ipc_platform_do_cmd(struct ipc *ipc); diff --git a/src/ipc/intel-ipc.c b/src/ipc/intel-ipc.c index e39951a..8e6482a 100644 --- a/src/ipc/intel-ipc.c +++ b/src/ipc/intel-ipc.c @@ -373,7 +373,7 @@ int ipc_stream_send_position(struct comp_dev *cdev, posn->comp_id = cdev->comp.id;
return ipc_queue_host_message(_ipc, posn->rhdr.hdr.cmd, posn, - sizeof(*posn), NULL, 0, NULL, NULL); + sizeof(*posn), NULL, 0, NULL, NULL, 1); }
/* send stream position TODO: send compound message */ @@ -385,7 +385,7 @@ int ipc_stream_send_xrun(struct comp_dev *cdev, posn->comp_id = cdev->comp.id;
return ipc_queue_host_message(_ipc, posn->rhdr.hdr.cmd, posn, - sizeof(*posn), NULL, 0, NULL, NULL); + sizeof(*posn), NULL, 0, NULL, NULL, 1); }
static int ipc_stream_trigger(uint32_t header) @@ -651,7 +651,7 @@ int ipc_dma_trace_send_position(void) posn.rhdr.hdr.size = sizeof(posn);
return ipc_queue_host_message(_ipc, posn.rhdr.hdr.cmd, &posn, - sizeof(posn), NULL, 0, NULL, NULL); + sizeof(posn), NULL, 0, NULL, NULL, 1); }
static int ipc_glb_debug_message(uint32_t header) @@ -909,19 +909,123 @@ static inline struct ipc_msg *msg_get_empty(struct ipc *ipc) return msg; }
+static inline struct ipc_msg *ipc_glb_stream_message_find(struct ipc *ipc, + struct sof_ipc_stream_posn *posn) +{ + struct list_item *plist; + struct ipc_msg *msg = NULL; + struct sof_ipc_stream_posn *old_posn = NULL; + uint32_t cmd; + + /* Check whether the command is expected */ + cmd = (posn->rhdr.hdr.cmd & SOF_CMD_TYPE_MASK) >> SOF_CMD_TYPE_SHIFT; + + switch (cmd) { + case iCS(SOF_IPC_STREAM_TRIG_XRUN): + case iCS(SOF_IPC_STREAM_POSITION): + /* + * jump out of the switch if it is expected command + * for subsequent searching in host message list + */ + break; + default: + /* return NULL for non-expected command */ + return msg; + } + + /* iterate host message list for searching */ + list_for_item(plist, &ipc->msg_list) { + msg = container_of(plist, struct ipc_msg, list); + if (msg->header == posn->rhdr.hdr.cmd) { + old_posn = (struct sof_ipc_stream_posn *)msg->tx_data; + if (old_posn->comp_id == posn->comp_id) + return msg; + } + } + + /* return NULL if no matched message */ + return msg; +} + +static inline struct ipc_msg *ipc_glb_trace_message_find(struct ipc *ipc, + struct sof_ipc_dma_trace_posn *posn) +{ + struct list_item *plist; + struct ipc_msg *msg = NULL; + uint32_t cmd; + + /* Check whether the command is expected */ + cmd = (posn->rhdr.hdr.cmd & SOF_CMD_TYPE_MASK) >> SOF_CMD_TYPE_SHIFT; + + switch (cmd) { + case iCS(SOF_IPC_TRACE_DMA_POSITION): + /* + * jump out of the switch if it is expected command + * for subsequent searching in host message list + */ + break; + default: + /* return NULL for non-expected command */ + return msg; + } + + /* iterate host message list for searching */ + list_for_item(plist, &ipc->msg_list) { + msg = container_of(plist, struct ipc_msg, list); + if (msg->header == posn->rhdr.hdr.cmd) + return msg; + } + + /* return NULL if no matched message */ + return msg; +} + +static inline struct ipc_msg *msg_find(struct ipc *ipc, uint32_t header, + void *tx_data) +{ + uint32_t type; + struct ipc_msg *msg = NULL; + + /* use different sub function for different global message type */ + type = (header & SOF_GLB_TYPE_MASK) >> SOF_GLB_TYPE_SHIFT; + + switch (type) { + case iGS(SOF_IPC_GLB_STREAM_MSG): + msg = ipc_glb_stream_message_find(ipc, + (struct sof_ipc_stream_posn *)tx_data); + break; + case iGS(SOF_IPC_GLB_TRACE_MSG): + msg = ipc_glb_trace_message_find(ipc, + (struct sof_ipc_dma_trace_posn *)tx_data); + break; + default: + /* return NULL for non-expected message type */ + break; + } + + return msg; +}
int ipc_queue_host_message(struct ipc *ipc, uint32_t header, void *tx_data, size_t tx_bytes, void *rx_data, - size_t rx_bytes, void (*cb)(void*, void*), void *cb_data) + size_t rx_bytes, void (*cb)(void*, void*), void *cb_data, uint32_t replace) { - struct ipc_msg *msg; - uint32_t flags; + struct ipc_msg *msg = NULL; + uint32_t flags, found = 0; int ret = 0;
spin_lock_irq(&ipc->lock, flags);
- /* get a free message */ - msg = msg_get_empty(ipc); + /* do we need to replace an existing message? */ + if (replace) + msg = msg_find(ipc, header, tx_data); + + /* do we need to use a new empty message? */ + if (msg) + found = 1; + else + msg = msg_get_empty(ipc); + if (msg == NULL) { trace_ipc_error("eQb"); ret = -EBUSY; @@ -939,9 +1043,11 @@ int ipc_queue_host_message(struct ipc *ipc, uint32_t header, if (tx_bytes > 0 && tx_bytes < SOF_IPC_MSG_MAX_SIZE) rmemcpy(msg->tx_data, tx_data, tx_bytes);
- /* now queue the message */ - ipc->dsp_pending = 1; - list_item_append(&msg->list, &ipc->msg_list); + if (!found) { + /* now queue the message */ + ipc->dsp_pending = 1; + list_item_append(&msg->list, &ipc->msg_list); + }
out: spin_unlock_irq(&ipc->lock, flags);