Skip to content

drivers/at: at out-of-band data support for at commands parser#8542

Merged
aabadie merged 2 commits intoRIOT-OS:masterfrom
OTAkeys:pr/at_oob
Jul 4, 2018
Merged

drivers/at: at out-of-band data support for at commands parser#8542
aabadie merged 2 commits intoRIOT-OS:masterfrom
OTAkeys:pr/at_oob

Conversation

@vincent-d
Copy link
Copy Markdown
Member

Contribution description

This adds a trick to support asynchronous AT command parsing.

The function at_process_oob can be called asynchronously to handle what has been received.
User code must register callbacks with at_add_oob. Internally it uses a clist

Issues/PRs references

Based on #7084

@vincent-d vincent-d added Type: new feature The issue requests / The PR implemements a new feature for RIOT State: waiting for other PR State: The PR requires another PR to be merged first labels Feb 9, 2018
@vincent-d vincent-d removed the State: waiting for other PR State: The PR requires another PR to be merged first label May 24, 2018
@vincent-d
Copy link
Copy Markdown
Member Author

Rebased

@tcschmidt
Copy link
Copy Markdown
Member

@aabadie would you like to take up reviewing now #7084 is merged?

@aabadie
Copy link
Copy Markdown
Contributor

aabadie commented May 29, 2018

@vincent-d, this needs rebase again now that #9185 is merged

@vincent-d
Copy link
Copy Markdown
Member Author

rebased

Copy link
Copy Markdown
Contributor

@aabadie aabadie left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm wondering if we could introduce a pseudo-module (at-oob ?) and make the new code compilation conditional. That would allow a very light default configuration if asynchronous behaviour is not needed.
What do you think ?

drivers/at/at.c Outdated
#include "xtimer.h"

#define ENABLE_DEBUG (0)
#define ENABLE_DEBUG (1)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Debugging is still activated, here and below

@vincent-d
Copy link
Copy Markdown
Member Author

I'm wondering if we could introduce a pseudo-module

+1

Unfortunately I think I won't be able to work on this this week nor next week.

Copy link
Copy Markdown
Contributor

@aabadie aabadie left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Found minor stuff. Otherwise looks good. I still need to test though.

const char *urc; /**< URC which must match */
void *arg; /**< optional argument */
} at_oob_t;
#endif
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe add a comment beside the endif.

typedef struct {
isrpipe_t isrpipe; /**< isrpipe used for getting data from uart */
uart_t uart; /**< UART device where the AT device is attached */
clist_node_t oob_list;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This attribute should be added only if MODULE_AT_OOB is loaded.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And it's not documented btw

Copy link
Copy Markdown
Contributor

@maxvankessel maxvankessel left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I 've used this in combination with a GSM driver and added my findings of this driver.

drivers/at/at.c Outdated

DEBUG("Trying to match with %s\n", oob->urc);

if (strncmp(buf, oob->urc, strlen(oob->urc)) == 0) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if oob->urc has zero characters (strlen would be zero). oob->urc matches buffer, because all characters are correct.

Shouldn't the length of the oob->urc be checked?

drivers/at/at.c Outdated
return 0;
}

void at_process_oob(at_dev_t *dev)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The name used by modem manufactures is URC (unsolicited result code), I think it should be renamed to at_process_urc

* @brief add a callback for an out-of-bound data
*
* @param[in] dev device to operate on
* @param[in] oob out-of-band value to register
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The user doesn't know that only the reference (of the at_oob_t) is stored, can we tell the user it must store the value?

drivers/at/at.c Outdated
size_t read = 0;

while (1) {
int res = isrpipe_try_read(&dev->isrpipe, buf + read, sizeof(buf) - read);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm uncertain if it's good method to bypass the isrpipe mutex. I know an URC can happen at any time, but the uart_init callback to character received is connected to isrpipe_write_one which unlocks the isrpipe mutex.

The way I used this method:
A gpio_int connected to the modem ring indicator. This signals a thread wakeup. Thread claims modem mutex and calls at_process_oob. And URC is dispatched to the correct callbacks.

If an URC is sent at any time, all data should be filtered for +urc:data EOL if the URC exists as callback, it should be stored and passed when at_process_oob is called. Otherwise the URC is discarded by active operations

image

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AFAIK, an URC cannot come at anytime (i.e. not during the process of a command).
I'm also not sure to fully understand your point, and I have the impression that your proposal would mean having 2 isrpipes.

@toonst will continue working on this topic on our side, so I guess you can keep this discussion with him ;)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see U-Blox has a well documented AT-manual. U-Blox states "module delayes URC during command execution." Quectel and Telit don't specify this....

Example:
An AT+COPS=? command can last for 180s, a SMS can be received in this period of time. Or an incomming call present itself.

I'm not saying it should be changed, I think it can be fixed if a problem occurs.

Copy link
Copy Markdown
Contributor

@maxvankessel maxvankessel Jun 12, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But are we going to bypass the isrpipe mutex? By calling try_read.

My proposal would be:

int res = at_readline(dev, buf, sizeof(buf), false, (uint32_t)(10 * US_PER_SEC));
if (res == 0) {
    /* skip possible empty line */
    res = at_readline(dev, buf, sizeof(buf), false, (uint32_t)(10 * US_PER_SEC));
}
if(res > 0) {
    p = buf;
    while ((*p == '\r' || *p == '\n' || *p == ' ') && ((size_t)(p - buf) < (uint8_t)res)) {
        p++;
    }
    clist_foreach(&dev->oob_list, _check_oob, p);
}

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't really understand the issue with 'bypassing' the isrpipe mutex. As far as I understand, this mutex is only there for synchronization and to block the caller thread if the pipe is empty.

In our case, we don't want to block if empty, so no need to wait for the mutex to be unlocked.

But as I read the isrpipe/tsrb code, I have troubles to understand how the tsrb is actually thread-safe...

To solve the 'an URC can come at any time' issue, we should process URC each time a command is sent and do as you suggested before I guess. But this make everything really complicated.

Copy link
Copy Markdown
Contributor

@maxvankessel maxvankessel Jun 12, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have a use case where the isrpipe should be locked:
I've left the isrpipe out of the drawing, but let the isrpipe->mutex in.

image

The use case is not 100% correct. But after a ring has been received, the thread should be blocked until de URC is complete.

Copy link
Copy Markdown
Member Author

@vincent-d vincent-d Jun 21, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure to have understood everything, but isrpipe_read first tries to read from the buffer, then lock the mutex if it needs to wait for more characters.

Then if your GSM thread has a higher priority than Main thread, it will try to read from the buffer regardless of the mutex state. So there are 2 cases:

  • you can read everything right away, so you check your URC and return
  • you cannot read everything and you lock the mutex, then the main thread will be scheduled first because it has locked the mutex before, so the URC (or a part of it) will go to the main thread

I have the feeling that we're trying to solve a problem without knowing if it really exists. I would start by locking the whole at_dev_t with a rmutex and consider that an URC cannot be received in the middle of a command.

Copy link
Copy Markdown
Contributor

@maxvankessel maxvankessel Jun 22, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@vincent-d let me try over (I will skip the URC in the middle of a command/request):

I think the at_process_urc doesn't work because isrpipe_try_read only returns the number of read bytes. The process is expecting a -1 if tsrb is dried out, but it doesn't return this option.

int res = isrpipe_try_read(&dev->isrpipe, buf + read,  sizeof(buf) - read);
if (!res) {
    return;
}

Also I'm using the ring functionality of the modem. This notifies the host something is incoming. So my thread can sleep and wake on a notify. Instead of running the 'at_process_urc` endless (as you did in your modem pr).

Between a ring indication and an URC is usually a few milliseconds, I would like a block functionality int the at_process_urc to block until a URC is received. e.g. void at_process_oob(at_dev_t *dev, uint32_t timeout).
If timeout is 0 it doesn't block, if timeout is set it's blocked until a URC is completed (with EOL).

I can make this proposal if you like. But is requires #9240 to be looked at, to check the tsrb content

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Alright, I understand, finally ^^. I'll try to come up with something, you can also make a proposal if you want.

But still, I'm wondering if a lock at the at_dev_t level wouldn't make sense too.

@vincent-d
Copy link
Copy Markdown
Member Author

@maxvankessel do you prefer like that?

@maxvankessel
Copy link
Copy Markdown
Contributor

This works for me :-)

@maxvankessel do you prefer like that?

@aabadie aabadie added Area: drivers Area: Device drivers CI: ready for build If set, CI server will compile all applications for all available boards for the labeled PR labels Jun 26, 2018
Copy link
Copy Markdown
Contributor

@aabadie aabadie left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me. Untested-ACK

Please squash

@toonst
Copy link
Copy Markdown
Member

toonst commented Jul 4, 2018

Seems good to me, can we merge this?

@aabadie aabadie merged commit 4ea93f3 into RIOT-OS:master Jul 4, 2018
@cladmi cladmi added this to the Release 2018.07 milestone Jul 10, 2018
@toonst toonst deleted the pr/at_oob branch August 21, 2018 15:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Area: drivers Area: Device drivers CI: ready for build If set, CI server will compile all applications for all available boards for the labeled PR Type: new feature The issue requests / The PR implemements a new feature for RIOT

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants