Updated: October 28, 2024 |
Run a script on a device
#include <sys/modem.h> int modem_script( int fd, struct modem_script* table, speed_t* baud, void (*io)( char* progress, char* in, char* out ), int (*cancel)(void) );
libc
Use the -l c option to qcc to link against this library. This library is usually included automatically.
The modem_script() function runs the script table on the device associated with the file descriptor fd. The script implements a simple state machine that emits strings and waits for responses.
Each string that's emitted or received is passed to the function io() as follows:
Call | Description |
---|---|
(*io)(str, 0, 0) | Emitted progress string |
(*io)(0, str, 0) | Received string |
(*io)(0, 0, str) | Emitted response string |
This lets an application set up a callback that can display the script's interaction in a status window.
If you provide a cancel function, it's called once each newquiet 1/10 of a second while waiting for input. If this function returns a nonzero value, the read returns immediately with -1 and errno is set to ETIMEDOUT. You can use the cancel function as a callback in a graphical dialer that needs to support a cancel button to stop a script.
The table is an array of modem_script structures that contain the following members:
Here's an example that demonstrates the operation of the script:
/* curstate curflags newstate newflags newtimeout newquiet retvalue pattern response */ struct modem_script table[] ={ {1, 0, 1, 0, 2, 5, 0, NULL, "ATZ\\r\\P0a"}, {1, 0, 2, 0, 30, 5, 0, "*ok*", "ATDT5910934"}, {2, MODEM_BAUD, 3, MODEM_LASTLINE, 10, 5, 0, "*connect*", NULL}, {3, 0, 4, 0, 8, 5, 0, "*login:*", "guest"}, {4, MODEM_NOECHO, 5, 0, 15, 5, 0, "*password:*", "xxxx"}, {5, 0, 0, 0, 0, 0, 0, "*$ *", NULL}, {0, 0, 0, 0, 0, 0, 1, "*no carrier*", NULL}, {0, 0, 0, 0, 0, 0, 2, "*no answer*", NULL}, {0, 0, 0, 0, 0, 0, 3, "*no dialtone*", NULL}, {0, 0, 0, 0, 0, 0, 4, "*busy*", NULL}, { NULL } };
When this script is passed to modem_script(), the current state is set to 1, and the output is ATZ (the response in the first array element).
While in any state, modem_script() waits for input, matching it against the current state or the wildcard state of 0.
State 1
Input | Action |
---|---|
*ok* | Go to state 2 and emit ATDT1-591-0934. The flags to be used in the new state are set to 0, the quiet time in the new state is set to 5/10 of a second, and the timeout time in the new state is set to 30 seconds. |
*no carrier* | Go to state 0 (the termination newstate), return with the contents of retvalue (1). |
*no answer* | Go to state 0 (the termination newstate), return with the contents of retvalue (2). |
*no dialtone* | Go to state 0 (the termination newstate), return with the contents of retvalue (3). |
*busy* | Go to state 0 (the termination newstate), return with the contents of retvalue (4). |
State 2
Input | Action |
---|---|
*connect* | Go to state 3 and don't emit anything to the device. The flags to be used in the new state are set to MODEM_LASTLINE, the quiet time in the new state is set to 5/10 of a second, and the timeout time in the new state is set to 10 seconds. Since the current flags are MODEM_BAUD, the baud rate is extracted from the connect message. |
*no carrier* | Same as previous table |
*no answer* | Same as previous table |
*no dialtone* | Same as previous table |
*busy* | Same as previous table |
State 3
Input | Action |
---|---|
*login* | Go to state 4 and emit guest. The flags to be used in the new state are set to 0, the quiet time in the new state is set to 5/10 of a second, and the timeout time in the new state is set to 8 seconds. |
*no carrier* | Same as previous table |
*no answer* | Same as previous table |
*no dialtone* | Same as previous table |
*busy* | Same as previous table |
State 4
Input | Action |
---|---|
*password* | Go to state 5 and emit xxxx. The flags to be used in the new state are set to 0, the quiet time in the new state is set to 5/10 of a second, and the timeout time in the new state is set to 15 seconds. Since the current flags are MODEM_NOECHO, the password response xxxx isn't sent to the io() callback. |
*no carrier* | Same as previous table |
*no answer* | Same as previous table |
*no dialtone* | Same as previous table |
*busy* | Same as previous table |
State 5
Input | Action |
---|---|
*$ * | Go to state 0 (the termination newstate), return with the contents of retvalue (0). |
*no carrier* | Same as previous table |
*no answer* | Same as previous table |
*no dialtone* | Same as previous table |
*busy* | Same as previous table |
If you set the flag MODEM_BAUD for a state, then any number embedded in a matching response is extracted and assigned as a number to the baud parameter.
If you don't set the flag MODEM_NOECHO for a state, then all emitted strings are also given to the passed io function as (*io)(0, 0, response).
The retvalue member of a script entry that terminates the script. This will always be a positive number. If modem_script fails, it returns -1 and sets errno.
Safety: | |
---|---|
Cancellation point | Yes |
Interrupt handler | No |
Signal handler | Read the Caveats |
Thread | Read the Caveats |
Depending on what you do in your cancel function, it might or might not be safe to call modem_script() from a signal handler or a multithreaded program.