Skip to content

Commit b2408d8

Browse files
author
Hasnain Virk
committed
Extending FileHandle & introducing mbed_poll
This has been an attempt to extend existing FileHandle to behave like POSIX file descriptor for input/output resources/devices too. This will now provide an abstract indicator/handle any arbitrary file or device type resource. May be in future, sockets too. In order to correctly detect availability of read/write a FileHandle, we needed a select or poll mechanisms. We opted for poll as POSIX defines in http://pubs.opengroup.org/onlinepubs/009695399/functions/poll.html Currently, mbed::poll() just spins and scans filehandles looking for any events we are interested in. In future, his spinning behaviour will be replaced with condition variables. In retarget.cpp we have introduced an mbed::fdopen() function which is equivalent to C fdopen(). It attaches a std stream to our FileHandle stream. newlib-nano somehow does not seem to call isatty() so retarget doesn't work for device type file handles. We handle this by checking ourselves in mbed::fdopen() if we wish to attach our stream to std stream. We also turn off buffering by C library as our stuff will be buffered already. sigio() is also provided, matching the API of the Socket class, with a view to future unification. As well as unblocking poll(), _poll_change calls the user sigio callback if an event happens, i.e., when FileHandle becomes readable/writable.
1 parent fb7cbdf commit b2408d8

File tree

7 files changed

+335
-18
lines changed

7 files changed

+335
-18
lines changed

drivers/Serial.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,23 @@ class Serial : public SerialBase, public Stream {
8181
*/
8282
Serial(PinName tx, PinName rx, int baud);
8383

84+
/* Stream gives us a FileHandle with non-functional poll()/readable()/writable. Pass through
85+
* the calls from the SerialBase instead for backwards compatibility. This problem is
86+
* part of why Stream and Serial should be deprecated.
87+
*/
88+
bool readable()
89+
{
90+
return SerialBase::readable();
91+
}
92+
bool writable()
93+
{
94+
return SerialBase::writeable();
95+
}
96+
bool writeable()
97+
{
98+
return SerialBase::writeable();
99+
}
100+
84101
protected:
85102
virtual int _getc();
86103
virtual int _putc(int c);

platform/FileHandle.cpp

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
/* mbed Microcontroller Library
2+
* Copyright (c) 2006-2016 ARM Limited
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
#include "FileHandle.h"
17+
#include "platform/mbed_retarget.h"
18+
#include "platform/mbed_critical.h"
19+
20+
namespace mbed {
21+
22+
off_t FileHandle::size()
23+
{
24+
/* remember our current position */
25+
off_t off = seek(0, SEEK_CUR);
26+
if (off < 0) {
27+
return off;
28+
}
29+
/* seek to the end to get the file length */
30+
off_t size = seek(0, SEEK_END);
31+
/* return to our old position */
32+
seek(off, SEEK_SET);
33+
return size;
34+
}
35+
36+
void FileHandle::sigio(Callback<void()> func) {
37+
core_util_critical_section_enter();
38+
_callback = func;
39+
if (_callback) {
40+
short current_events = poll(0x7FFF);
41+
if (current_events) {
42+
_callback();
43+
}
44+
}
45+
core_util_critical_section_exit();
46+
}
47+
48+
std::FILE *fdopen(FileHandle *fh, const char *mode)
49+
{
50+
return mbed_fdopen(fh, mode);
51+
}
52+
53+
} // namespace mbed

platform/FileHandle.h

Lines changed: 113 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/* mbed Microcontroller Library
2-
* Copyright (c) 2006-2013 ARM Limited
2+
* Copyright (c) 2017 ARM Limited
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -18,7 +18,9 @@
1818

1919
typedef int FILEHANDLE;
2020

21-
#include <stdio.h>
21+
#include <cstdio>
22+
#include "Callback.h"
23+
#include "platform/mbed_poll.h"
2224
#include "platform/platform.h"
2325

2426
namespace mbed {
@@ -40,6 +42,12 @@ class FileHandle {
4042
virtual ~FileHandle() {}
4143

4244
/** Read the contents of a file into a buffer
45+
*
46+
* Devices acting as FileHandles should follow POSIX semantics:
47+
*
48+
* * if no data is available, and non-blocking set return -EAGAIN
49+
* * if no data is available, and blocking set, wait until data is available
50+
* * If any data is available, call returns immediately
4351
*
4452
* @param buffer The buffer to read in to
4553
* @param size The number of bytes to read
@@ -62,7 +70,7 @@ class FileHandle {
6270
* SEEK_SET to start from beginning of file,
6371
* SEEK_CUR to start from current position in file,
6472
* SEEK_END to start from end of file
65-
* @return The new offset of the file
73+
* @return The new offset of the file, negative error code on failure
6674
*/
6775
virtual off_t seek(off_t offset, int whence = SEEK_SET) = 0;
6876

@@ -84,6 +92,8 @@ class FileHandle {
8492
/** Check if the file in an interactive terminal device
8593
*
8694
* @return True if the file is a terminal
95+
* @return False if the file is not a terminal
96+
* @return Negative error code on failure
8797
*/
8898
virtual int isatty()
8999
{
@@ -94,7 +104,7 @@ class FileHandle {
94104
*
95105
* @note This is equivalent to seek(0, SEEK_CUR)
96106
*
97-
* @return The current offset in the file
107+
* @return The current offset in the file, negative error code on failure
98108
*/
99109
virtual off_t tell()
100110
{
@@ -114,13 +124,7 @@ class FileHandle {
114124
*
115125
* @return Size of the file in bytes
116126
*/
117-
virtual off_t size()
118-
{
119-
off_t off = tell();
120-
off_t size = seek(0, SEEK_END);
121-
seek(off, SEEK_SET);
122-
return size;
123-
}
127+
virtual off_t size();
124128

125129
/** Move the file position to a given offset from a given location.
126130
*
@@ -133,7 +137,10 @@ class FileHandle {
133137
* -1 on failure or unsupported
134138
*/
135139
MBED_DEPRECATED_SINCE("mbed-os-5.4", "Replaced by FileHandle::seek")
136-
virtual off_t lseek(off_t offset, int whence) { return seek(offset, whence); }
140+
virtual off_t lseek(off_t offset, int whence)
141+
{
142+
return seek(offset, whence);
143+
}
137144

138145
/** Flush any buffers associated with the FileHandle, ensuring it
139146
* is up to date on disk
@@ -143,17 +150,109 @@ class FileHandle {
143150
* -1 on error
144151
*/
145152
MBED_DEPRECATED_SINCE("mbed-os-5.4", "Replaced by FileHandle::sync")
146-
virtual int fsync() { return sync(); }
153+
virtual int fsync()
154+
{
155+
return sync();
156+
}
147157

148158
/** Find the length of the file
149159
*
150160
* @returns
151161
* Length of the file
152162
*/
153163
MBED_DEPRECATED_SINCE("mbed-os-5.4", "Replaced by FileHandle::size")
154-
virtual off_t flen() { return size(); }
164+
virtual off_t flen()
165+
{
166+
return size();
167+
}
168+
169+
/** Set blocking or non-blocking mode of the file operation like read/write.
170+
* Definition depends upon the subclass implementing FileHandle.
171+
* The default is blocking.
172+
*
173+
* @param blocking true for blocking mode, false for non-blocking mode.
174+
*/
175+
virtual int set_blocking(bool blocking)
176+
{
177+
return -1;
178+
}
179+
180+
/** Check for poll event flags
181+
* The input parameter can be used or ignored - the could always return all events,
182+
* or could check just the events listed in events.
183+
* Call is non-blocking - returns instantaneous state of events.
184+
* Whenever an event occurs, the derived class must call _poll_change().
185+
* @param events bitmask of poll events we're interested in - POLLIN/POLLOUT etc.
186+
*
187+
* @returns
188+
* bitmask of poll events that have occurred.
189+
*/
190+
virtual short poll(short events) const
191+
{
192+
// Possible default for real files
193+
return POLLIN | POLLOUT;
194+
}
195+
196+
/** Returns true if the FileHandle is writable.
197+
* Definition depends upon the subclass implementing FileHandle.
198+
* For example, if the FileHandle is of type Stream, writable() could return
199+
* true when there is ample buffer space available for write() calls.
200+
*/
201+
bool writable() const
202+
{
203+
return poll(POLLOUT) & POLLOUT;
204+
}
205+
206+
/** Returns true if the FileHandle is readable.
207+
* Definition depends upon the subclass implementing FileHandle.
208+
* For example, if the FileHandle is of type Stream, readable() could return
209+
* true when there is something available to read.
210+
*/
211+
bool readable() const
212+
{
213+
return poll(POLLIN) & POLLIN;
214+
}
215+
216+
/** Register a callback on state change of the file.
217+
*
218+
* The specified callback will be called on state changes such as when
219+
* the file can be written to or read from.
220+
*
221+
* The callback may be called in an interrupt context and should not
222+
* perform expensive operations.
223+
*
224+
* Note! This is not intended as an attach-like asynchronous api, but rather
225+
* as a building block for constructing such functionality.
226+
*
227+
* The exact timing of when the registered function
228+
* is called is not guaranteed and susceptible to change. It should be used
229+
* as a cue to make read/write/poll calls to find the current state.
230+
*
231+
* @param func Function to call on state change
232+
*/
233+
void sigio(Callback<void()> func);
234+
235+
/** Issue sigio to user - used by mbed::_poll_change */
236+
void _send_sigio()
237+
{
238+
if (_callback) {
239+
_callback();
240+
}
241+
}
242+
243+
private:
244+
Callback<void()> _callback;
155245
};
156246

247+
/** Not a member function
248+
* This call is equivalent to posix fdopen().
249+
* Returns a pointer to std::FILE.
250+
* It associates a Stream to an already opened file descriptor (FileHandle)
251+
*
252+
* @param fh, a pointer to an opened file descriptor
253+
* @param mode, operation upon the file descriptor, e.g., 'wb+'*/
254+
255+
std::FILE *fdopen(FileHandle *fh, const char *mode);
157256

158257
} // namespace mbed
159258

platform/mbed_poll.cpp

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
/* mbed Microcontroller Library
2+
* Copyright (c) 2017 ARM Limited
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
#include "mbed_poll.h"
17+
#include "FileHandle.h"
18+
#include "Timer.h"
19+
#ifdef MBED_CONF_RTOS_PRESENT
20+
#include "rtos/Thread.h"
21+
#endif
22+
23+
namespace mbed {
24+
25+
// timeout -1 forever, or milliseconds
26+
int poll(pollfh fhs[], unsigned nfhs, int timeout)
27+
{
28+
/**
29+
* TODO Proper wake-up mechanism.
30+
* In order to correctly detect availability of read/write a FileHandle, we needed
31+
* a select or poll mechanisms. We opted for poll as POSIX defines in
32+
* http://pubs.opengroup.org/onlinepubs/009695399/functions/poll.html Currently,
33+
* mbed::poll() just spins and scans filehandles looking for any events we are
34+
* interested in. In future, his spinning behaviour will be replaced with
35+
* condition variables.
36+
*/
37+
Timer timer;
38+
if (timeout > 0) {
39+
timer.start();
40+
}
41+
42+
int count = 0;
43+
for (;;) {
44+
/* Scan the file handles */
45+
for (unsigned n = 0; n < nfhs; n++) {
46+
FileHandle *fh = fhs[n].fh;
47+
short mask = fhs[n].events | POLLERR | POLLHUP | POLLNVAL;
48+
if (fh) {
49+
fhs[n].revents = fh->poll(mask) & mask;
50+
} else {
51+
fhs[n].revents = POLLNVAL;
52+
}
53+
if (fhs[n].revents) {
54+
count++;
55+
}
56+
}
57+
58+
if (count) {
59+
break;
60+
}
61+
62+
/* Nothing selected - this is where timeout handling would be needed */
63+
if (timeout == 0 || (timeout > 0 && timer.read_ms() > timeout)) {
64+
break;
65+
}
66+
#ifdef MBED_CONF_RTOS_PRESENT
67+
// TODO - proper blocking
68+
// wait for condition variable, wait queue whatever here
69+
rtos::Thread::yield();
70+
#endif
71+
}
72+
return count;
73+
}
74+
75+
void _poll_change(FileHandle *fh)
76+
{
77+
// TODO, will depend on how we implement poll
78+
79+
// Also, do the user callback
80+
fh->_send_sigio();
81+
}
82+
83+
} // namespace mbed

0 commit comments

Comments
 (0)