Commit | Line | Data |
---|---|---|
172d42d9 AG |
1 | /* mbed Microcontroller Library |
2 | * Copyright (c) 2006-2013 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 "platform.h" | |
17 | #include "FileHandle.h" | |
18 | #include "FileSystemLike.h" | |
19 | #include "FilePath.h" | |
20 | #include "serial_api.h" | |
21 | #include <errno.h> | |
22 | ||
23 | #if defined(__ARMCC_VERSION) | |
24 | # include <rt_sys.h> | |
25 | # define PREFIX(x) _sys##x | |
26 | # define OPEN_MAX _SYS_OPEN | |
27 | # ifdef __MICROLIB | |
28 | # pragma import(__use_full_stdio) | |
29 | # endif | |
30 | ||
31 | #elif defined(__ICCARM__) | |
32 | # include <yfuns.h> | |
33 | # define PREFIX(x) _##x | |
34 | # define OPEN_MAX 16 | |
35 | ||
36 | # define STDIN_FILENO 0 | |
37 | # define STDOUT_FILENO 1 | |
38 | # define STDERR_FILENO 2 | |
39 | ||
40 | #else | |
41 | # include <sys/stat.h> | |
42 | # include <sys/unistd.h> | |
43 | # include <sys/syslimits.h> | |
44 | # define PREFIX(x) x | |
45 | #endif | |
46 | ||
47 | using namespace mbed; | |
48 | ||
49 | extern const char __stdin_name[] = "/stdin"; | |
50 | extern const char __stdout_name[] = "/stdout"; | |
51 | extern const char __stderr_name[] = "/stderr"; | |
52 | ||
53 | /* newlib has the filehandle field in the FILE struct as a short, so | |
54 | * we can't just return a Filehandle* from _open and instead have to | |
55 | * put it in a filehandles array and return the index into that array | |
56 | * (or rather index+3, as filehandles 0-2 are stdin/out/err). | |
57 | */ | |
58 | static FileHandle *filehandles[OPEN_MAX]; | |
59 | ||
60 | FileHandle::~FileHandle() { | |
61 | /* Remove all open filehandles for this */ | |
62 | for (unsigned int fh_i = 0; fh_i < sizeof(filehandles)/sizeof(*filehandles); fh_i++) { | |
63 | if (filehandles[fh_i] == this) { | |
64 | filehandles[fh_i] = NULL; | |
65 | } | |
66 | } | |
67 | } | |
68 | ||
69 | #if DEVICE_SERIAL | |
70 | extern int stdio_uart_inited; | |
71 | extern serial_t stdio_uart; | |
72 | #endif | |
73 | ||
74 | static void init_serial() { | |
75 | #if DEVICE_SERIAL | |
76 | if (stdio_uart_inited) return; | |
77 | serial_init(&stdio_uart, STDIO_UART_TX, STDIO_UART_RX); | |
78 | serial_format(&stdio_uart, 8, ParityNone, 1); | |
79 | serial_baud(&stdio_uart, 9600); | |
80 | #endif | |
81 | } | |
82 | ||
83 | static inline int openmode_to_posix(int openmode) { | |
84 | int posix = openmode; | |
85 | #ifdef __ARMCC_VERSION | |
86 | if (openmode & OPEN_PLUS) { | |
87 | posix = O_RDWR; | |
88 | } else if(openmode & OPEN_W) { | |
89 | posix = O_WRONLY; | |
90 | } else if(openmode & OPEN_A) { | |
91 | posix = O_WRONLY|O_APPEND; | |
92 | } else { | |
93 | posix = O_RDONLY; | |
94 | } | |
95 | /* a, w, a+, w+ all create if file does not already exist */ | |
96 | if (openmode & (OPEN_A|OPEN_W)) { | |
97 | posix |= O_CREAT; | |
98 | } | |
99 | /* w and w+ truncate */ | |
100 | if (openmode & OPEN_W) { | |
101 | posix |= O_TRUNC; | |
102 | } | |
103 | #elif defined(__ICCARM__) | |
104 | switch (openmode & _LLIO_RDWRMASK) { | |
105 | case _LLIO_RDONLY: posix = O_RDONLY; break; | |
106 | case _LLIO_WRONLY: posix = O_WRONLY; break; | |
107 | case _LLIO_RDWR : posix = O_RDWR ; break; | |
108 | } | |
109 | if (openmode & _LLIO_CREAT ) posix |= O_CREAT; | |
110 | if (openmode & _LLIO_APPEND) posix |= O_APPEND; | |
111 | if (openmode & _LLIO_TRUNC ) posix |= O_TRUNC; | |
112 | #endif | |
113 | return posix; | |
114 | } | |
115 | ||
116 | extern "C" FILEHANDLE PREFIX(_open)(const char* name, int openmode) { | |
117 | /* Use the posix convention that stdin,out,err are filehandles 0,1,2. | |
118 | */ | |
119 | if (std::strcmp(name, __stdin_name) == 0) { | |
120 | init_serial(); | |
121 | return 0; | |
122 | } else if (std::strcmp(name, __stdout_name) == 0) { | |
123 | init_serial(); | |
124 | return 1; | |
125 | } else if (std::strcmp(name,__stderr_name) == 0) { | |
126 | init_serial(); | |
127 | return 2; | |
128 | } | |
129 | ||
130 | // find the first empty slot in filehandles | |
131 | unsigned int fh_i; | |
132 | for (fh_i = 0; fh_i < sizeof(filehandles)/sizeof(*filehandles); fh_i++) { | |
133 | if (filehandles[fh_i] == NULL) break; | |
134 | } | |
135 | if (fh_i >= sizeof(filehandles)/sizeof(*filehandles)) { | |
136 | return -1; | |
137 | } | |
138 | ||
139 | FileHandle *res; | |
140 | ||
141 | /* FILENAME: ":0x12345678" describes a FileLike* */ | |
142 | if (name[0] == ':') { | |
143 | void *p; | |
144 | sscanf(name, ":%p", &p); | |
145 | res = (FileHandle*)p; | |
146 | ||
147 | /* FILENAME: "/file_system/file_name" */ | |
148 | } else { | |
149 | FilePath path(name); | |
150 | ||
151 | if (path.isFile()) { | |
152 | res = path.file(); | |
153 | } else { | |
154 | FileSystemLike *fs = path.fileSystem(); | |
155 | if (fs == NULL) return -1; | |
156 | int posix_mode = openmode_to_posix(openmode); | |
157 | res = fs->open(path.fileName(), posix_mode); /* NULL if fails */ | |
158 | } | |
159 | } | |
160 | ||
161 | if (res == NULL) return -1; | |
162 | filehandles[fh_i] = res; | |
163 | ||
164 | return fh_i + 3; // +3 as filehandles 0-2 are stdin/out/err | |
165 | } | |
166 | ||
167 | extern "C" int PREFIX(_close)(FILEHANDLE fh) { | |
168 | if (fh < 3) return 0; | |
169 | ||
170 | FileHandle* fhc = filehandles[fh-3]; | |
171 | filehandles[fh-3] = NULL; | |
172 | if (fhc == NULL) return -1; | |
173 | ||
174 | return fhc->close(); | |
175 | } | |
176 | ||
177 | #if defined(__ICCARM__) | |
178 | extern "C" size_t __write (int fh, const unsigned char *buffer, size_t length) { | |
179 | #else | |
180 | extern "C" int PREFIX(_write)(FILEHANDLE fh, const unsigned char *buffer, unsigned int length, int mode) { | |
181 | #endif | |
182 | int n; // n is the number of bytes written | |
183 | if (fh < 3) { | |
184 | #if DEVICE_SERIAL | |
185 | if (!stdio_uart_inited) init_serial(); | |
186 | for (unsigned int i = 0; i < length; i++) { | |
187 | serial_putc(&stdio_uart, buffer[i]); | |
188 | } | |
189 | #endif | |
190 | n = length; | |
191 | } else { | |
192 | FileHandle* fhc = filehandles[fh-3]; | |
193 | if (fhc == NULL) return -1; | |
194 | ||
195 | n = fhc->write(buffer, length); | |
196 | } | |
197 | #ifdef __ARMCC_VERSION | |
198 | return length-n; | |
199 | #else | |
200 | return n; | |
201 | #endif | |
202 | } | |
203 | ||
204 | #if defined(__ICCARM__) | |
205 | extern "C" size_t __read (int fh, unsigned char *buffer, size_t length) { | |
206 | #else | |
207 | extern "C" int PREFIX(_read)(FILEHANDLE fh, unsigned char *buffer, unsigned int length, int mode) { | |
208 | #endif | |
209 | int n; // n is the number of bytes read | |
210 | if (fh < 3) { | |
211 | // only read a character at a time from stdin | |
212 | #if DEVICE_SERIAL | |
213 | *buffer = serial_getc(&stdio_uart); | |
214 | #endif | |
215 | n = 1; | |
216 | } else { | |
217 | FileHandle* fhc = filehandles[fh-3]; | |
218 | if (fhc == NULL) return -1; | |
219 | ||
220 | n = fhc->read(buffer, length); | |
221 | } | |
222 | #ifdef __ARMCC_VERSION | |
223 | return length-n; | |
224 | #else | |
225 | return n; | |
226 | #endif | |
227 | } | |
228 | ||
229 | #ifdef __ARMCC_VERSION | |
230 | extern "C" int PREFIX(_istty)(FILEHANDLE fh) | |
231 | #else | |
232 | extern "C" int _isatty(FILEHANDLE fh) | |
233 | #endif | |
234 | { | |
235 | /* stdin, stdout and stderr should be tty */ | |
236 | if (fh < 3) return 1; | |
237 | ||
238 | FileHandle* fhc = filehandles[fh-3]; | |
239 | if (fhc == NULL) return -1; | |
240 | ||
241 | return fhc->isatty(); | |
242 | } | |
243 | ||
244 | extern "C" | |
245 | #if defined(__ARMCC_VERSION) | |
246 | int _sys_seek(FILEHANDLE fh, long position) | |
247 | #elif defined(__ICCARM__) | |
248 | long __lseek(int fh, long offset, int whence) | |
249 | #else | |
250 | int _lseek(FILEHANDLE fh, int offset, int whence) | |
251 | #endif | |
252 | { | |
253 | if (fh < 3) return 0; | |
254 | ||
255 | FileHandle* fhc = filehandles[fh-3]; | |
256 | if (fhc == NULL) return -1; | |
257 | ||
258 | #if defined(__ARMCC_VERSION) | |
259 | return fhc->lseek(position, SEEK_SET); | |
260 | #else | |
261 | return fhc->lseek(offset, whence); | |
262 | #endif | |
263 | } | |
264 | ||
265 | #ifdef __ARMCC_VERSION | |
266 | extern "C" int PREFIX(_ensure)(FILEHANDLE fh) { | |
267 | if (fh < 3) return 0; | |
268 | ||
269 | FileHandle* fhc = filehandles[fh-3]; | |
270 | if (fhc == NULL) return -1; | |
271 | ||
272 | return fhc->fsync(); | |
273 | } | |
274 | ||
275 | extern "C" long PREFIX(_flen)(FILEHANDLE fh) { | |
276 | if (fh < 3) return 0; | |
277 | ||
278 | FileHandle* fhc = filehandles[fh-3]; | |
279 | if (fhc == NULL) return -1; | |
280 | ||
281 | return fhc->flen(); | |
282 | } | |
283 | #endif | |
284 | ||
285 | ||
286 | #if !defined(__ARMCC_VERSION) && !defined(__ICCARM__) | |
287 | extern "C" int _fstat(int fd, struct stat *st) { | |
288 | if ((STDOUT_FILENO == fd) || (STDERR_FILENO == fd) || (STDIN_FILENO == fd)) { | |
289 | st->st_mode = S_IFCHR; | |
290 | return 0; | |
291 | } | |
292 | ||
293 | errno = EBADF; | |
294 | return -1; | |
295 | } | |
296 | #endif | |
297 | ||
298 | namespace std { | |
299 | extern "C" int remove(const char *path) { | |
300 | FilePath fp(path); | |
301 | FileSystemLike *fs = fp.fileSystem(); | |
302 | if (fs == NULL) return -1; | |
303 | ||
304 | return fs->remove(fp.fileName()); | |
305 | } | |
306 | ||
307 | extern "C" int rename(const char *oldname, const char *newname) { | |
c73a6a41 DM |
308 | FilePath a(oldname); |
309 | FilePath b(newname); | |
310 | if (!a.fileSystem() || a.fileSystem() != b.fileSystem()) return -1; | |
6d877d9b | 311 | |
c73a6a41 | 312 | return a.fileSystem()->rename(a.fileName(), b.fileName()); |
172d42d9 AG |
313 | } |
314 | ||
315 | extern "C" char *tmpnam(char *s) { | |
316 | return NULL; | |
317 | } | |
318 | ||
319 | extern "C" FILE *tmpfile() { | |
320 | return NULL; | |
321 | } | |
322 | } // namespace std | |
323 | ||
324 | #ifdef __ARMCC_VERSION | |
325 | extern "C" char *_sys_command_string(char *cmd, int len) { | |
326 | return NULL; | |
327 | } | |
328 | #endif | |
329 | ||
330 | extern "C" DIR *opendir(const char *path) { | |
331 | /* root dir is FileSystemLike */ | |
332 | if (path[0] == '/' && path[1] == 0) { | |
333 | return FileSystemLike::opendir(); | |
334 | } | |
335 | ||
336 | FilePath fp(path); | |
337 | FileSystemLike* fs = fp.fileSystem(); | |
338 | if (fs == NULL) return NULL; | |
339 | ||
340 | return fs->opendir(fp.fileName()); | |
341 | } | |
342 | ||
343 | extern "C" struct dirent *readdir(DIR *dir) { | |
344 | return dir->readdir(); | |
345 | } | |
346 | ||
347 | extern "C" int closedir(DIR *dir) { | |
348 | return dir->closedir(); | |
349 | } | |
350 | ||
351 | extern "C" void rewinddir(DIR *dir) { | |
352 | dir->rewinddir(); | |
353 | } | |
354 | ||
355 | extern "C" off_t telldir(DIR *dir) { | |
356 | return dir->telldir(); | |
357 | } | |
358 | ||
359 | extern "C" void seekdir(DIR *dir, off_t off) { | |
360 | dir->seekdir(off); | |
361 | } | |
362 | ||
363 | extern "C" int mkdir(const char *path, mode_t mode) { | |
364 | FilePath fp(path); | |
365 | FileSystemLike *fs = fp.fileSystem(); | |
366 | if (fs == NULL) return -1; | |
367 | ||
368 | return fs->mkdir(fp.fileName(), mode); | |
369 | } | |
370 | ||
371 | #if defined(TOOLCHAIN_GCC_CR) || defined(TOOLCHAIN_GCC_CS) || defined(TOOLCHAIN_GCC_ARM) | |
372 | /* prevents the exception handling name demangling code getting pulled in */ | |
373 | #include "error.h" | |
374 | namespace __gnu_cxx { | |
375 | void __verbose_terminate_handler() { | |
376 | error("Exception"); | |
377 | } | |
378 | } | |
379 | #endif | |
380 | ||
381 | // Make sure we are pulling in the retargeting module at link time | |
382 | volatile int stdio_retargeting_module; |