I want to define an API to a task used as a hw driver, where any other task can post commands by means of a queue. The command is executed in the context of the hw driver task and a few seconds later, after some network data is received, a response needs to be sent to the task that originated the command, which is waiting for the response (blocked). What is the usual pattern to achieve this?
I guess the task that issued the command can send a pointer to memory where the response is written by the hw driver task, right?
Shall the task that issued the command pend on a semaphore?
I understand that there are many ways of doing this, and I would like to know what is the usual pattern followed with freertos.
The first question is does the API actually NEED to use a separate task, or can it just be a function called by the task that runs in the context of the task. (That might depend on if the hw driver needs code running at a specific task priority). The driver can use a Mutex so that only 1 copy of the function is doing active work at any given time. A lot of my drivers work this way.
If you do use a separate task, blocking on a semaphore is common, or you could include the task handle for the task making the request and then block on a task notification. The request can also include the data pointer.
Thanks. I have to use a separate driver task because it needs to wait continuously on some network events (this is handling an AT modem).
I lean towards using a queue that includes a data pointer, together with the task handle. Then, right after posting to the queue, we wait on a task notification so that the driver can notify us that the command has been processed. If I understand well, that is what you mean.
Yes, that is one option, but it does mean that this task needs to wait on two diffent queue so you will want to use a Queueset.
One comment, from what I hear here, it sounds like the receive side and send side are at least somewhat uncoupled, and a structure that might make sense is a task handling receiving messages, and a transmit side drive that runs in the context of the task sending the data, that perhaps has an array of pending operations that the receiver side uses to forward answers it gets.
I see, so your transmit side drive would avoid the need for an additional command queue, so that we don’t need to use a QueueSet. Also we would not need any task notification construct.
In this case, this driver transmit function would run in the context of the task sending the data:
wait on uart response1 (sending task blocks)
tx uart command2
wait on uart response2 (sending task blocks)
Did I get you right?
Something like that, but you would likely need a mutex around each of the sends, and before sending use some sort of structure to register to the receive task who and where to put the response. Then after sending, block for the answer.
This page shows an alternative to using a queue set. The model shown there can be extended to include the task handle of the source task in the structure that is sent to the queue - so the task can then block on a task notification.