Skip to content

Commit 207800c

Browse files
author
Amanda Butler
authored
Create FileHandle.md
Apply changes from PR #745 to 5.11 to create API reference for FileHandle to fill gap in documentation.
1 parent f8e94e6 commit 207800c

File tree

1 file changed

+216
-0
lines changed

1 file changed

+216
-0
lines changed

docs/api/platform/FileHandle.md

Lines changed: 216 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,216 @@
1+
## FileHandle
2+
3+
<span class="images">![](https://os.mbed.com/docs/mbed-os/v5.11/mbed-os-api-doxy/classmbed_1_1_file_handle.png)<span>FileHandle class hierarchy</span></span>
4+
5+
For general information on [files](file.html) and [filing systems](filesystem.html) in persistent storage, see their documentation. This chapter covers the abstract API, with an emphasis on devices.
6+
7+
`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`.
8+
9+
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`.
10+
11+
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:
12+
13+
- The console input and output streams (`stdin` and `stdout`).
14+
- The `ATCmdParser` helper.
15+
- The PPP connection to lwIP.
16+
17+
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.
18+
19+
#### Relationship of FileHandle to other APIs
20+
21+
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.
22+
23+
<span class="images">![](https://s3-us-west-2.amazonaws.com/mbed-os-docs-images/filehandle_callstack.png)</span>
24+
25+
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.
26+
27+
The three APIs provide different levels of capability:
28+
29+
API | C/C++ | POSIX | Mbed
30+
-------------------------------|-------------------------|------------------------|-----------------------------
31+
Headers | stdio.h, iostream | mbed_retarget.h | FileHandle.h, mbed_poll.h
32+
Main type | FILE * | int | FileHandle object
33+
Blocking I/O | Yes (always) | Yes (default) | Yes (default)
34+
Nonblocking I/O | No | Yes | Yes
35+
Poll | No | Yes (struct pollfd) | Yes (struct pollfh)
36+
Sigio | No | No | Yes
37+
Device-specific extensions | No | No | Possible using derived types
38+
Newline conversion | Yes (enabled with JSON) | No | No
39+
Error indications | EOF, ferror, set errno | Return -1, set errno | Return negative error code
40+
Portability | High | Medium | Low
41+
42+
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.
43+
44+
<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>
45+
46+
#### Mapping between APIs
47+
48+
Calls are provided to attach already-opened lower levels to the higher levels:
49+
50+
- `int mbed_bind_to_fd(FileHandle *)` bind a FileHandle to a POSIX file descriptor.
51+
- `FILE *fdopen(int fd, const char *mode)` bind a POSIX file descriptor to a stdio FILE.
52+
- `FILE *fdopen(FileHandle *fh, const char *mode)` bind a FileHandle to a stdio FILE.
53+
54+
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.
55+
56+
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..
57+
58+
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.
59+
60+
#### Redirecting the console
61+
62+
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`.
63+
64+
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:
65+
66+
```
67+
namespace mbed
68+
{
69+
FileHandle *mbed_target_override_console(int)
70+
{
71+
static SerialWireOutput swo;
72+
return &swo;
73+
}
74+
}
75+
```
76+
77+
Then any program using `printf` on that target sends its output over the SWO, rather than serial.
78+
79+
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`.
80+
81+
```
82+
// Don't do:
83+
Serial serial(USBTX, USBRX);
84+
serial.printf("Hello!\r\n");
85+
86+
// Do do:
87+
printf("Hello!\n"); // assume platform.stdio-convert-newlines is true
88+
```
89+
90+
Beyond the target-specific override, an application can override the target's default behavior itself by providing `mbed::mbed_override_console`.
91+
92+
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.
93+
94+
#### Polling and nonblocking
95+
96+
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.
97+
98+
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.
99+
100+
For a timed wait for data, or to monitor multiple `FileHandle`s, see [`poll`](poll.html)
101+
102+
#### Event-driven I/O
103+
104+
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.
105+
106+
Important notes on sigio:
107+
108+
- 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`.
109+
- 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.
110+
- Spurious sigios are permitted - you can't assume data will be available after a sigio.
111+
- Given all the above, use of sigio normally implies use of nonblocking mode or possibly `poll`.
112+
113+
Ordinary files do not generate sigio callbacks because they are always readable and writable.
114+
115+
#### Stream-derived FileHandles
116+
117+
`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.
118+
119+
Note that `FileHandle` implementations derived from `Stream`, such as `Serial`, have various limitations:
120+
121+
- `Stream` does not support nonblocking I/O, poll or sigio.
122+
- `Stream` does not have correct `read` semantics for a device - it always waits for the entire input buffer to fill.
123+
- `Stream` returns 0 from `isatty`, which can slightly confuse the C library (for example defeating newline conversion and causing buffering).
124+
125+
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.
126+
127+
### FileHandle class reference
128+
129+
[![View code](https://www.mbed.com/embed/?type=library)](http://os.mbed.com/docs/v5.11/mbed-os-api-doxy/classmbed_1_1_file_handle.html)
130+
131+
### FileHandle using C library example
132+
133+
```
134+
// Continuously monitor a serial device, and every time it outputs a
135+
// character, send it to the console and toggle LED2. Can use the C library
136+
// to access the device as only using blocking I/O.
137+
//
138+
// Note that the console is accessed using putchar - this will be accessing
139+
// a FileHandle-based device under the surface, but the particular device can be
140+
// target-dependent. This makes the program portable to different devices
141+
// with different console types, with the only target-dependence being
142+
// knowledge of which pins the serial device we're monitoring is attached to,
143+
// which can be configured using JSON.
144+
145+
static DigitalOut led2(LED2);
146+
147+
// UARTSerial derives from FileHandle
148+
static UARTSerial device(MBED_CONF_APP_DEVICE_TX, MBED_CONF_APP_DEVICE_RX);
149+
150+
int main()
151+
{
152+
// Perform device-specific setup
153+
device.set_baud(19200);
154+
155+
// Once set up, access through the C library
156+
FILE *devin = fdopen(&device, "r");
157+
158+
while (1) {
159+
putchar(fgetc(devin));
160+
led2 = !led2;
161+
}
162+
}
163+
```
164+
165+
### FileHandle sigio example
166+
167+
```
168+
// Main thread flashes LED1, while we monitor a serial-attached device
169+
// in the background. Every time that device outputs a character, we echo
170+
// it to the console and toggle LED2.
171+
#include "mbed.h"
172+
173+
static DigitalOut led1(LED1);
174+
static DigitalOut led2(LED2);
175+
176+
static UARTSerial device(MBED_CONF_APP_DEVICE_TX, MBED_CONF_APP_DEVICE_RX);
177+
178+
static void callback_ex()
179+
{
180+
// always read until data is exhausted - we may not get another
181+
// sigio otherwise
182+
while (1) {
183+
char c;
184+
if (device.read(&c, 1) != 1) {
185+
break;
186+
}
187+
putchar(c);
188+
led2 = !led2;
189+
}
190+
}
191+
192+
int main()
193+
{
194+
// UARTSerial-specific method - all others are from FileHandle base class
195+
device.set_baud(19200);
196+
197+
// Ensure that device.read() returns -EAGAIN when out of data
198+
device.set_blocking(false);
199+
200+
// sigio callback is deferred to event queue, as we cannot in general
201+
// perform read() calls directly from the sigio() callback.
202+
device.sigio(mbed_event_queue()->event(callback_ex));
203+
204+
while (1) {
205+
led1 = !led1;
206+
wait(0.5);
207+
}
208+
}
209+
210+
```
211+
212+
### Related content
213+
214+
- [File](file.html).
215+
- [FileSystem](filesystem.html).
216+
- [Poll](poll.html).

0 commit comments

Comments
 (0)