| 1 | /* Copyright 2012 Adam Green (http://mbed.org/users/AdamGreen/)\r |
| 2 | \r |
| 3 | Licensed under the Apache License, Version 2.0 (the "License");\r |
| 4 | you may not use this file except in compliance with the License.\r |
| 5 | You may obtain a copy of the License at\r |
| 6 | \r |
| 7 | http://www.apache.org/licenses/LICENSE-2.0\r |
| 8 | \r |
| 9 | Unless required by applicable law or agreed to in writing, software\r |
| 10 | distributed under the License is distributed on an "AS IS" BASIS,\r |
| 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r |
| 12 | See the License for the specific language governing permissions and\r |
| 13 | limitations under the License.\r |
| 14 | */\r |
| 15 | /* System calls called out as overrideable in newlib documentation which weren't implemented follow in this list\r |
| 16 | with reason:\r |
| 17 | \r |
| 18 | environ Not required. getenv() and setenv() can be used without providing an override as there is a default\r |
| 19 | _execve Standard C APIs implemented by newlib don't end up calling this function. Implemented _system\r |
| 20 | instead.\r |
| 21 | _fork Standard C APIs implemented by newlib don't end up calling this function. Implemented _system\r |
| 22 | instead.\r |
| 23 | _link This is a POSIX routine and while newlib will call it for the standard C rename() function, this\r |
| 24 | particular routine is overridden in mbed.ar where it just returns -1.\r |
| 25 | _stat This is a system API and not part of the C standard. It doesn't appear to be required for mbed.\r |
| 26 | _times Appears to be a system API and not part of the C standard. Not called from standard APIs.\r |
| 27 | _unlink This is a POSIX routine. newlib will call it for standard C remove(), this particular API is\r |
| 28 | already overidden by mbed.ar\r |
| 29 | */\r |
| 30 | #include <reent.h>\r |
| 31 | #include <errno.h>\r |
| 32 | #include <stdlib.h> /* abort */\r |
| 33 | #include <sys/types.h>\r |
| 34 | #include <sys/stat.h>\r |
| 35 | #include <sys/_default_fcntl.h>\r |
| 36 | #include "LPC17xx.h" /* for _get_*SP() from core_cm3.h*/\r |
| 37 | #include "mbedsys.h" /* for _sys_*() functions implemented in mbed/capi.ar */\r |
| 38 | #include "error.h" /* for error() panic routine */\r |
| 39 | #include "mri.h"\r |
| 40 | \r |
| 41 | \r |
| 42 | #undef errno\r |
| 43 | extern int errno;\r |
| 44 | \r |
| 45 | \r |
| 46 | /* Flag used to track whether the stdin/stdout/stderr handles have been opened yet */\r |
| 47 | static int g_StandardHandlesOpened = 0;\r |
| 48 | \r |
| 49 | \r |
| 50 | /* Open the stdin/stdout/stderr handles */\r |
| 51 | extern "C" void __GCC4MBEDOpenStandardHandles(void)\r |
| 52 | {\r |
| 53 | /* Open stdin/stdout/stderr */\r |
| 54 | _sys_open("/stdin", OPENMODE_R);\r |
| 55 | _sys_open("/stdout", OPENMODE_W);\r |
| 56 | _sys_open("/stderr", OPENMODE_W);\r |
| 57 | \r |
| 58 | g_StandardHandlesOpened = 1;\r |
| 59 | } \r |
| 60 | \r |
| 61 | \r |
| 62 | extern "C" int _kill(int pid, int sig)\r |
| 63 | {\r |
| 64 | /* Avoid warnings */\r |
| 65 | (void)pid;\r |
| 66 | (void)sig;\r |
| 67 | \r |
| 68 | errno = EINVAL;\r |
| 69 | return -1;\r |
| 70 | }\r |
| 71 | \r |
| 72 | extern "C" void _exit(int status)\r |
| 73 | {\r |
| 74 | /* Call the mbed version of exit found in capi.ar */\r |
| 75 | exit(status);\r |
| 76 | }\r |
| 77 | \r |
| 78 | extern "C" int _getpid(void)\r |
| 79 | {\r |
| 80 | return 1;\r |
| 81 | }\r |
| 82 | \r |
| 83 | \r |
| 84 | extern char _end; /* Defined by the linker */\r |
| 85 | static char *heap_end;\r |
| 86 | \r |
| 87 | static char* get_stack_top(void)\r |
| 88 | {\r |
| 89 | return (char*) __get_MSP();\r |
| 90 | }\r |
| 91 | \r |
| 92 | extern "C" caddr_t _sbrk(int incr)\r |
| 93 | {\r |
| 94 | char *prev_heap_end;\r |
| 95 | if (heap_end == 0) {\r |
| 96 | heap_end = &_end;\r |
| 97 | }\r |
| 98 | prev_heap_end = heap_end;\r |
| 99 | if (heap_end + incr > get_stack_top()) {\r |
| 100 | \r |
| 101 | abort();\r |
| 102 | }\r |
| 103 | heap_end += incr;\r |
| 104 | return (caddr_t) prev_heap_end;\r |
| 105 | }\r |
| 106 | \r |
| 107 | extern "C" int _open(const char *name, int flags, int mode) \r |
| 108 | {\r |
| 109 | int OpenMode = 0;\r |
| 110 | \r |
| 111 | (void)mode;\r |
| 112 | \r |
| 113 | /* Convert from newlib flags to mbed openmode */\r |
| 114 | if (flags & O_RDWR)\r |
| 115 | {\r |
| 116 | OpenMode |= OPENMODE_PLUS;\r |
| 117 | }\r |
| 118 | if (flags & O_TRUNC)\r |
| 119 | {\r |
| 120 | OpenMode |= OPENMODE_W;\r |
| 121 | }\r |
| 122 | if (flags & O_APPEND)\r |
| 123 | {\r |
| 124 | OpenMode |= OPENMODE_A;\r |
| 125 | }\r |
| 126 | \r |
| 127 | return _sys_open(name, OpenMode);\r |
| 128 | }\r |
| 129 | \r |
| 130 | extern "C" int _close(int file)\r |
| 131 | {\r |
| 132 | return _sys_close(file);\r |
| 133 | }\r |
| 134 | \r |
| 135 | extern "C" int _isatty(int file)\r |
| 136 | {\r |
| 137 | /* Hardcoding the stdin/stdout/stderr handles to be interactive tty devices, unlike mbed.ar */\r |
| 138 | if (file < 3)\r |
| 139 | {\r |
| 140 | return 1;\r |
| 141 | }\r |
| 142 | \r |
| 143 | return _sys_istty(file);\r |
| 144 | }\r |
| 145 | \r |
| 146 | /* TODO: Remove this later when _lseek no longer requires it */\r |
| 147 | static unsigned short _crc16(const unsigned char* pBuffer, size_t BufferSizeInBytes)\r |
| 148 | {\r |
| 149 | unsigned short CRC = 0;\r |
| 150 | \r |
| 151 | while (BufferSizeInBytes--)\r |
| 152 | {\r |
| 153 | unsigned short Byte = (unsigned short)*pBuffer++;\r |
| 154 | int i;\r |
| 155 | \r |
| 156 | for (i = 0 ; i < 8 ; i++)\r |
| 157 | {\r |
| 158 | unsigned short Value = (Byte ^ CRC) & 1;\r |
| 159 | CRC >>= 1;\r |
| 160 | if (Value)\r |
| 161 | {\r |
| 162 | CRC ^= 0xA001;\r |
| 163 | }\r |
| 164 | Byte >>= 1;\r |
| 165 | }\r |
| 166 | }\r |
| 167 | \r |
| 168 | return ~CRC;\r |
| 169 | }\r |
| 170 | \r |
| 171 | extern "C" int _lseek(int file, int ptr, int dir) \r |
| 172 | {\r |
| 173 | /* Function pointer type for the FileHandle::lseek() method */\r |
| 174 | typedef int (*lseekMethod)(void* pThis, int ptr, int dir);\r |
| 175 | static const unsigned short ExpectedSysSeekCRC = 0xDFC0;\r |
| 176 | static lseekMethod*** pppFileHandles = NULL;\r |
| 177 | lseekMethod** ppFileHandle;\r |
| 178 | lseekMethod* pFileHandleVTable;\r |
| 179 | lseekMethod FileHandle_lseekMethod;\r |
| 180 | \r |
| 181 | /* When file is set to 0, 1, or 2 then the file is actually a standard I/O stream so just return 0 since they\r |
| 182 | don't really support seeking and this is what _sys_seek() does as well */\r |
| 183 | if (file < 3)\r |
| 184 | {\r |
| 185 | return 0;\r |
| 186 | }\r |
| 187 | \r |
| 188 | /* Find the location of the filehandles array if we haven't already */\r |
| 189 | if (!pppFileHandles)\r |
| 190 | {\r |
| 191 | /* TODO: This code is pretty hacky but it is the only way I can get things to work with mbed.ar at this time.\r |
| 192 | If the mbed crew could make a small change to their library such that they exposed something equivalent\r |
| 193 | to _sys_lseek in addition to their _sys_seek then both this _lseek function and their _sys_seek could \r |
| 194 | call it to perform seeks using any seek direction (whence) value */\r |
| 195 | unsigned short* pSysSeek;\r |
| 196 | unsigned short CRC;\r |
| 197 | \r |
| 198 | /* Grab a pointer to where the _sys_seek code ends up being loaded for this program */\r |
| 199 | pSysSeek = (unsigned short*)((unsigned int)_sys_seek & ~1);\r |
| 200 | \r |
| 201 | /* Perform a CRC over the first 40 bytes which contain PC-relative code for the _sys_seek function to\r |
| 202 | make sure that it hasn't changed from when I figured out the location of the pointer used for\r |
| 203 | dereferencing into the 16 element filehandles static array from stdio.cpp */\r |
| 204 | CRC = _crc16((unsigned char*)pSysSeek, 40);\r |
| 205 | if (ExpectedSysSeekCRC != CRC)\r |
| 206 | {\r |
| 207 | error("RIP: _sys_seek() in mbed.ar has been modified\r\n"\r |
| 208 | " and _lseek in gcc4mbed/syscalls.c needs to be updated\r\n");\r |
| 209 | }\r |
| 210 | \r |
| 211 | /* I know that the pointer for the filehandles array is stored as a literal 20 shorts after the beginning of the \r |
| 212 | _sys_seek() function */\r |
| 213 | pppFileHandles = (lseekMethod***)*(void**)(void*)(pSysSeek + 20);\r |
| 214 | }\r |
| 215 | \r |
| 216 | \r |
| 217 | /* Find the pointer to the FileHandle object representing this file */\r |
| 218 | ppFileHandle = pppFileHandles[file - 3];\r |
| 219 | if (!ppFileHandle)\r |
| 220 | {\r |
| 221 | /* The file handle isn't valid */\r |
| 222 | return -1;\r |
| 223 | }\r |
| 224 | \r |
| 225 | /* The vtable for this FileHandle object will be stored in the first 4-byte word of the object */\r |
| 226 | pFileHandleVTable = *ppFileHandle;\r |
| 227 | \r |
| 228 | /* The fifth element of this vtable is the pointer to the lseek method */\r |
| 229 | FileHandle_lseekMethod = pFileHandleVTable[4];\r |
| 230 | \r |
| 231 | /* Finally call into the file handle object's seek method */\r |
| 232 | return FileHandle_lseekMethod(ppFileHandle, ptr, dir);\r |
| 233 | }\r |
| 234 | \r |
| 235 | extern "C" int _read(int file, char *ptr, int len)\r |
| 236 | {\r |
| 237 | int BytesNotRead;\r |
| 238 | \r |
| 239 | /* Open stdin/stdout/stderr if needed */\r |
| 240 | if (MRI_SEMIHOST_STDIO && file < 3)\r |
| 241 | {\r |
| 242 | return __mriNewlib_SemihostRead(file, ptr, len);\r |
| 243 | }\r |
| 244 | else if (!g_StandardHandlesOpened && file < 3)\r |
| 245 | {\r |
| 246 | __GCC4MBEDOpenStandardHandles();\r |
| 247 | }\r |
| 248 | \r |
| 249 | /* Call the function in mbed.ar and let it handle the read */\r |
| 250 | BytesNotRead = _sys_read(file, (unsigned char*)ptr, len, 0);\r |
| 251 | \r |
| 252 | /* EOF is indicated by setting the most significant bit so mask it off */\r |
| 253 | BytesNotRead &= 0x7FFFFFFF;\r |
| 254 | \r |
| 255 | /* The mbed version of the function returns bytes not read and newlib wants bytes read count */\r |
| 256 | return len - BytesNotRead;\r |
| 257 | }\r |
| 258 | \r |
| 259 | extern "C" int _write(int file, char *ptr, int len)\r |
| 260 | {\r |
| 261 | int BytesNotWritten;\r |
| 262 | \r |
| 263 | /* Open stdin/stdout/stderr if needed */\r |
| 264 | if (MRI_SEMIHOST_STDIO && file < 3)\r |
| 265 | {\r |
| 266 | return __mriNewlib_SemihostWrite(file, ptr, len);\r |
| 267 | }\r |
| 268 | else if (!g_StandardHandlesOpened && file < 3)\r |
| 269 | {\r |
| 270 | __GCC4MBEDOpenStandardHandles();\r |
| 271 | }\r |
| 272 | \r |
| 273 | /* Call the function in mbed.ar and let it handle the writes */\r |
| 274 | BytesNotWritten = _sys_write(file, (const unsigned char*)ptr, len, 0);\r |
| 275 | \r |
| 276 | /* The mbed version of the function returns bytes not written and newlib wants byte written count */\r |
| 277 | if (BytesNotWritten >= 0)\r |
| 278 | {\r |
| 279 | return len - BytesNotWritten;\r |
| 280 | }\r |
| 281 | else\r |
| 282 | {\r |
| 283 | /* In this case it did return an error */\r |
| 284 | return BytesNotWritten;\r |
| 285 | }\r |
| 286 | }\r |
| 287 | \r |
| 288 | extern "C" int _fstat(int file, struct stat *st)\r |
| 289 | {\r |
| 290 | (void)file;\r |
| 291 | \r |
| 292 | st->st_mode = S_IFCHR;\r |
| 293 | return 0;\r |
| 294 | }\r |
| 295 | \r |
| 296 | extern "C" int _system(const char* pCommand)\r |
| 297 | {\r |
| 298 | if (NULL == pCommand)\r |
| 299 | {\r |
| 300 | /* There is no command interpreter on the mbed */\r |
| 301 | return 0;\r |
| 302 | }\r |
| 303 | else\r |
| 304 | {\r |
| 305 | /* Indicate that command couldn't be executed since there is no command interpreter */\r |
| 306 | errno = ENOENT;\r |
| 307 | return -1;\r |
| 308 | }\r |
| 309 | }\r |