Skip to content

Commit 2c4f50e

Browse files
author
Amanda Butler
authored
Merge pull request #745 from kjbracey-arm/FileHandle
Add FileHandle/poll documentation
2 parents e754bc7 + 5569e24 commit 2c4f50e

File tree

3 files changed

+297
-0
lines changed

3 files changed

+297
-0
lines changed

docs/api/platform/FileHandle.md

Lines changed: 214 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,214 @@
1+
## FileHandle
2+
3+
For general information on [files](file.html) and [filing systems](filesystem.html) in persistant storage, see their documentation. This chapter covers the abstract API, with an emphasis on devices.
4+
5+
`FileHandle` is an abstract class representing a device that supports file-like operations, such as `read` and `write`. This may be an actual `File` on a storage device provided by a `FileSystem` or a device such as `UARTSerial`.
6+
7+
The `FileHandle` abstraction represents an already-opened file or device, so it has no `open` method of its own - the opening may take the form of a call to another class that returns a `FileHandle`, such as `FileSystem::open`, or it may be implicit in the construction of an object such as `UARTSerial`.
8+
9+
The `FileHandle` abstraction permits stream-handling code to be device-independent, rather than tied to a specific device like a serial port. Examples of such code in Mbed OS are:
10+
11+
- The console input and output streams (`stdin` and `stdout`).
12+
- The `ATCmdParser` helper.
13+
- The PPP connection to lwIP.
14+
15+
Exactly which operations a `FileHandle` supports depends on the underlying device, and in turn restricts what applications it is suitable for. For example, a database application might require random-access and `seek`, but this may not be available on a limited file system, and certainly not on a stream device. Only a `File` on a full `FileSystem`, such as `FATFileSystem`, would generally implement the entire API. Specialized devices may have particular limitations or behavior, which limit their general utility. Devices that do not implement a particular call indicate it by an error return - often `ENOSYS`, but sometimes more specific errors, such as `ESPIPE` apply; please see the POSIX specifications for details.
16+
17+
### Relationship of FileHandle to other APIs
18+
19+
You can use a `FileHandle` directly, or you can use standard POSIX or C/C++ APIs to manipulate it. Stdio calls taking `FILE *stream` call the POSIX APIs taking `int fd`, which call methods on `FileHandle` objects.
20+
21+
<span class="images">![](https://s3-us-west-2.amazonaws.com/mbed-os-docs-images/filehandle_callstack.png)</span>
22+
23+
The `FileHandle` may be implicitly created by a higher layer, as in a call to `fopen`. In this case, the name lookup produces a `FileHandle` and POSIX file descriptor internally.
24+
25+
The three APIs provide different levels of capability:
26+
27+
API | C/C++ | POSIX | Mbed
28+
-------------------------------|-------------------------|------------------------|-----------------------------
29+
Headers | stdio.h, iostream | mbed_retarget.h | FileHandle.h, mbed_poll.h
30+
Main type | FILE * | int | FileHandle object
31+
Blocking I/O | Yes (always) | Yes (default) | Yes (default)
32+
Nonblocking I/O | No | Yes | Yes
33+
Poll | No | Yes (struct pollfd) | Yes (struct pollfh)
34+
Sigio | No | No | Yes
35+
Device-specific extensions | No | No | Possible using derived types
36+
Newline conversion | Yes (enabled with JSON) | No | No
37+
Error indications | EOF, ferror, set errno | Return -1, set errno | Return negative error code
38+
Portability | High | Medium | Low
39+
40+
You can mix the APIs if you're careful, for example setting up a callback initially with `FileHandle::sigio` but performing all subsequent operations using POSIX.
41+
42+
<span class="notes">**Note:** `errno` is not thread-local on all toolchains. This may cause problems with error handling if multiple threads are using POSIX or C file APIs simultaneously.</span>
43+
44+
### Mapping between APIs
45+
46+
Calls are provided to attach already-opened lower levels to the higher levels:
47+
48+
- `int mbed_bind_to_fd(FileHandle *)` bind a FileHandle to a POSIX file descriptor.
49+
- `FILE *fdopen(int fd, const char *mode)` bind a POSIX file descriptor to a stdio FILE.
50+
- `FILE *fdopen(FileHandle *fh, const char *mode)` bind a FileHandle to a stdio FILE.
51+
52+
The standard POSIX function `int fileno(FILE *stream)` may be available to map from `FILE` to file descriptor, depending on the toolchain and C library in use - it is not usable in fully portable Mbed OS code.
53+
54+
As it is not possible to map from higher levels to lower levels, if code needs to access the lower levels, use a lower-level open call, so the lower-level handle is known. Then, bind that to the higher level..
55+
56+
The POSIX file descriptors for the console are available as `STDIN_FILENO`, `STDOUT_FILENO` and `STDERR_FILENO`, permitting operations such as `fsync(STDERR_FILENO)`, which would for example drain `UARTSerial`s output buffer.
57+
58+
### Redirecting the console
59+
60+
If a target has serial support, by default a serial port is used for the console. The pins and settings for the port selection come from target header files and JSON settings. This uses either an internal `DirectSerial` if unbuffered (for backwards compatibility) or `UARTSerial` if `platform.stdio-buffered-serial` is `true`.
61+
62+
The target can override this by providing `mbed::mbed_target_override_console` to specify an alternative `FileHandle`. For example, a target using SWO might have:
63+
64+
```
65+
namespace mbed
66+
{
67+
FileHandle *mbed_target_override_console(int)
68+
{
69+
static SerialWireOutput swo;
70+
return &swo;
71+
}
72+
}
73+
```
74+
75+
Then any program using `printf` on that target sends its output over the SWO, rather than serial.
76+
77+
Because targets can redirect the console in this way, portable applications should not use constructs like `Serial(USBTX, USBRX)`, assuming that this will access the console. Instead they should use `stdin`/`stdout`/`stderr` or `STDIN_FILENO`/`STDOUT_FILENO`/`STDERR_FILENO`.
78+
79+
```
80+
// Don't do:
81+
Serial serial(USBTX, USBRX);
82+
serial.printf("Hello!\r\n");
83+
84+
// Do do:
85+
printf("Hello!\n"); // assume platform.stdio-convert-newlines is true
86+
```
87+
88+
Beyond the target-specific override, an application can override the target's default behavior itself by providing `mbed::mbed_override_console`.
89+
90+
Alternatively, an application could use the standard C `freopen` function to redirect `stdout` to a named file or device while running. However there is no `fdreopen` analogue to redirect to an unnamed device by file descriptor or `FileHandle` pointer.
91+
92+
### Polling and nonblocking
93+
94+
By default `FileHandle`s conventionally block until a `read` or `write` operation completes. This is the only behavior supported by normal `File`s, and is expected by the C library's `stdio` functions.
95+
96+
Device-type `FileHandle`s, such as `UARTSerial`, are expected to also support nonblocking operation, which permits the `read` and `write` calls to return immediately when unable to transfer data. Please see the API reference pages of these functions for more information.
97+
98+
For a timed wait for data, or to monitor multiple `FileHandle`s, see [`poll`](poll.html)
99+
100+
### Event-driven I/O
101+
102+
If using nonblocking I/O, you probably want to know when to next attempt a `read` or `write` if they indicate no data is available. `FileHandle::sigio` lets you attach a `Callback`, which is called whenever the `FileHandle` becomes readable or writable.
103+
104+
Important notes on sigio:
105+
106+
- The sigio may be issued from interrupt context. You cannot portably issue `read` or `write` calls directly from this callback, so you should queue an [`Event`](event.html) or wake a thread to perform the `read` or `write`.
107+
- The sigio callback is only guaranteed when a `FileHandle` _becomes_ readable or writable. If you do not fully drain the input or fully fill the output, no sigio may be generated. This is also important on start-up - don't wait for sigio before attempting to read or write for the first time, but only use it as a "try again" signal after seeing an `EAGAIN` error.
108+
- Spurious sigios are permitted - you can't assume data will be available after a sigio.
109+
- Given all the above, use of sigio normally implies use of nonblocking mode or possibly `poll`.
110+
111+
Ordinary files do not generate sigio callbacks because they are always readable and writable.
112+
113+
### Stream-derived FileHandles
114+
115+
`Stream` is a legacy class that provides an abstract interface for streams similar to the `FileHandle` class. The difference is that the `Stream` API is built around the `getc` and `putc` set of functions, whereas `FileHandle` is built around `read` and `write`. This makes implementations simpler but limits what is possible with the API. Because of this, implementing the `FileHandle` API directly is suggested API for new device drivers.
116+
117+
Note that `FileHandle` implementations derived from `Stream`, such as `Serial`, have various limitations:
118+
119+
- `Stream` does not support nonblocking I/O, poll or sigio.
120+
- `Stream` does not have correct `read` semantics for a device - it always waits for the entire input buffer to fill.
121+
- `Stream` returns 0 from `isatty`, which can slightly confuse the C library (for example defeating newline conversion and causing buffering).
122+
123+
As such, you can only use `Stream`-based devices for blocking I/O, such as through the C library, so we don't recommend use of `Stream` to implement a `FileHandle` for more general use.
124+
125+
### FileHandle class reference
126+
127+
[![View code](https://www.mbed.com/embed/?type=library)](http://os-doc-builder.test.mbed.com/docs/development/mbed-os-api-doxy/classmbed_1_1_file_handle.html)
128+
129+
### FileHandle using C library example
130+
131+
```
132+
// Continuously monitor a serial device, and every time it outputs a
133+
// character, send it to the console and toggle LED2. Can use the C library
134+
// to access the device as only using blocking I/O.
135+
//
136+
// Note that the console is accessed using putchar - this will be accessing
137+
// a FileHandle-based device under the surface, but the particular device can be
138+
// target-dependent. This makes the program portable to different devices
139+
// with different console types, with the only target-dependence being
140+
// knowledge of which pins the serial device we're monitoring is attached to,
141+
// which can be configured using JSON.
142+
143+
static DigitalOut led2(LED2);
144+
145+
// UARTSerial derives from FileHandle
146+
static UARTSerial device(MBED_CONF_APP_DEVICE_TX, MBED_CONF_APP_DEVICE_RX);
147+
148+
int main()
149+
{
150+
// Perform device-specific setup
151+
device.set_baud(19200);
152+
153+
// Once set up, access through the C library
154+
FILE *devin = fdopen(&device, "r");
155+
156+
while (1) {
157+
putchar(fgetc(devin));
158+
led2 = !led2;
159+
}
160+
}
161+
```
162+
163+
### FileHandle sigio example
164+
165+
```
166+
// Main thread flashes LED1, while we monitor a serial-attached device
167+
// in the background. Every time that device outputs a character, we echo
168+
// it to the console and toggle LED2.
169+
#include "mbed.h"
170+
171+
static DigitalOut led1(LED1);
172+
static DigitalOut led2(LED2);
173+
174+
static UARTSerial device(MBED_CONF_APP_DEVICE_TX, MBED_CONF_APP_DEVICE_RX);
175+
176+
static void callback_ex()
177+
{
178+
// always read until data is exhausted - we may not get another
179+
// sigio otherwise
180+
while (1) {
181+
char c;
182+
if (device.read(&c, 1) != 1) {
183+
break;
184+
}
185+
putchar(c);
186+
led2 = !led2;
187+
}
188+
}
189+
190+
int main()
191+
{
192+
// UARTSerial-specific method - all others are from FileHandle base class
193+
device.set_baud(19200);
194+
195+
// Ensure that device.read() returns -EAGAIN when out of data
196+
device.set_blocking(false);
197+
198+
// sigio callback is deferred to event queue, as we cannot in general
199+
// perform read() calls directly from the sigio() callback.
200+
device.sigio(mbed_event_queue()->event(callback_ex));
201+
202+
while (1) {
203+
led1 = !led1;
204+
wait(0.5);
205+
}
206+
}
207+
208+
```
209+
210+
### Related content
211+
212+
- [File](file.html).
213+
- [FileSystem](filesystem.html).
214+
- [Poll](poll.html).

docs/api/platform/Poll.md

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
## Poll
2+
3+
The `poll` functions perform timed waits on one or more [file handles](filehandle.html).
4+
5+
Mbed OS provides `poll()` in two forms:
6+
7+
- `int poll(struct pollfd fds[], nfds_t nfds, int timeout)` the standard POSIX form taking an array of integer file descriptors.
8+
- `int mbed::poll(pollfh fhs[], unsigned nfhs, int timeout)`, the variant taking an array of `FileHandle *`.
9+
10+
See the POSIX specification of `poll` for full details of the API. Mbed OS provides the `POLLIN`, `POLLOUT`, `POLLERR` and `POLLHUP` event flags, but implementation depends on the device.
11+
12+
Note that the `poll` is not affected by the blocking or nonblocking setting of the individual file handles. The `timeout` argument always determines the wait behavior: 0 for immediate (nonblocking) return, -1 for wait forever or positive for a limited wait.
13+
14+
As per the POSIX specification, `poll` always indicates that ordinary files (as opposed to devices) are readable and writable.
15+
16+
### Poll reference
17+
18+
[![View code](https://www.mbed.com/embed/?type=library)](https://os-doc-builder.test.mbed.com/docs/development/mbed-os-api-doxy/group__platform__poll.html)
19+
20+
### Poll example
21+
22+
```
23+
// Transfer bidirectional data between two ports, acting as a virtual link.
24+
// poll() is used to monitor both ports for input.
25+
#include "mbed.h"
26+
27+
// Pins for each port are specified using mbed_app.json. Assume no flow control.
28+
// (If there were flow control, being blocked by it could break the implementation,
29+
// as you would stop reading input in the opposite direction).
30+
UARTSerial device1(MBED_CONF_APP_UART1_TX, MBED_CONF_APP_UART1_RX);
31+
UARTSerial device2(MBED_CONF_APP_UART2_TX, MBED_CONF_APP_UART2_RX);
32+
33+
// Precondition: "in" is readable
34+
static void copy_some(FileHandle *out, FileHandle *in)
35+
{
36+
// To ensure performance, try to read multiple bytes at once,
37+
// but don't expect to read many in practice.
38+
char buffer[32];
39+
40+
// Despite the FileHandle being in its default blocking mode,
41+
// read() must return immediately with between 1-32 bytes, as
42+
// you've already checked that `in` is ready with poll()
43+
ssize_t read = in->read(buffer, sizeof buffer);
44+
if (read <= 0) {
45+
error("Input error");
46+
}
47+
48+
// Write everything out. Assuming output port is same speed as input,
49+
// and no flow control, this may block briefly but not significantly.
50+
ssize_t written = out->write(buffer, read);
51+
if (written < read) {
52+
error("Output error");
53+
}
54+
}
55+
56+
int main()
57+
{
58+
char buffer[32];
59+
pollfh fds[2];
60+
61+
fds[0].fh = &device1;
62+
fds[0].events = POLLIN;
63+
fds[1].fh = &device2;
64+
fds[1].events = POLLIN;
65+
66+
while (1) {
67+
// Block indefinitely until either of the 2 ports is readable (or has an error)
68+
poll(fds, 2, -1);
69+
70+
// Transfer some data in either or both directions
71+
if (fds[0].revents) {
72+
copy_some(fds[1].fh, fds[0].fh);
73+
}
74+
if (fds[1].revents) {
75+
copy_some(fds[0].fh, fds[1].fh);
76+
}
77+
}
78+
}
79+
```
80+
81+
### Related content
82+
83+
- [FileHandle](filehandle.html).

docs/images/filehandle_callstack.png

39.5 KB
Loading

0 commit comments

Comments
 (0)