Home | History | Annotate | Download | only in inc
      1 /*
      2  * Copyright (C) 2016 The Android Open Source Project
      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 
     17 #ifndef _SYSCALL_DO_H_
     18 #define _SYSCALL_DO_H_
     19 
     20 
     21 #ifdef __cplusplus
     22 extern "C" {
     23 #endif
     24 
     25 #ifdef _OS_BUILD_
     26     #error "Syscalls should not be called from OS code"
     27 #endif
     28 
     29 #include <cpu/inc/syscallDo.h>
     30 #include <sensors.h>
     31 #include <syscall.h>
     32 #include <stdarg.h>
     33 #include <gpio.h>
     34 #include <osApi.h>
     35 #include <seos.h>
     36 #include <util.h>
     37 
     38 
     39 /* it is always safe to use this, but using syscallDo0P .. syscallDo4P macros may produce faster code for free */
     40 static inline uintptr_t syscallDoGeneric(uint32_t syscallNo, ...)
     41 {
     42     uintptr_t ret;
     43     va_list vl;
     44 
     45     va_start(vl, syscallNo);
     46     #ifdef SYSCALL_PARAMS_PASSED_AS_PTRS
     47         ret = cpuSyscallDo(syscallNo, &vl);
     48     #else
     49         ret = cpuSyscallDo(syscallNo, *(uint32_t*)&vl);
     50     #endif
     51     va_end(vl);
     52 
     53     return ret;
     54 }
     55 
     56 #ifdef cpuSyscallDo0P
     57     #define syscallDo0P(syscallNo) cpuSyscallDo0P(syscallNo)
     58 #else
     59     #define syscallDo0P(syscallNo) syscallDoGeneric(syscallNo)
     60 #endif
     61 
     62 #ifdef cpuSyscallDo1P
     63     #define syscallDo1P(syscallNo,p1) cpuSyscallDo1P(syscallNo,p1)
     64 #else
     65     #define syscallDo1P(syscallNo,p1) syscallDoGeneric(syscallNo,p1)
     66 #endif
     67 
     68 #ifdef cpuSyscallDo2P
     69     #define syscallDo2P(syscallNo,p1,p2) cpuSyscallDo2P(syscallNo,p1,p2)
     70 #else
     71     #define syscallDo2P(syscallNo,p1,p2) syscallDoGeneric(syscallNo,p1,p2)
     72 #endif
     73 
     74 #ifdef cpuSyscallDo3P
     75     #define syscallDo3P(syscallNo,p1,p2,p3) cpuSyscallDo3P(syscallNo,p1,p2,p3)
     76 #else
     77     #define syscallDo3P(syscallNo,p1,p2,p3) syscallDoGeneric(syscallNo,p1,p2,p3)
     78 #endif
     79 
     80 #ifdef cpuSyscallDo4P
     81     #define syscallDo4P(syscallNo,p1,p2,p3,p4) cpuSyscallDo4P(syscallNo,p1,p2,p3,p4)
     82 #else
     83     #define syscallDo4P(syscallNo,p1,p2,p3,p4) syscallDoGeneric(syscallNo,p1,p2,p3,p4)
     84 #endif
     85 
     86 #ifdef cpuSyscallDo5P
     87     #define syscallDo5P(syscallNo,p1,p2,p3,p4,p5) cpuSyscallDo5P(syscallNo,p1,p2,p3,p4,p5)
     88 #else
     89     #define syscallDo5P(syscallNo,p1,p2,p3,p4,p5) syscallDoGeneric(syscallNo,p1,p2,p3,p4,p5)
     90 #endif
     91 
     92 
     93 
     94 //system syscalls live here
     95 static inline bool eOsEventSubscribe(uint32_t tid, uint32_t evtType)
     96 {
     97     return syscallDo2P(SYSCALL_NO(SYSCALL_DOMAIN_OS, SYSCALL_OS_MAIN, SYSCALL_OS_MAIN_EVENTQ, SYSCALL_OS_MAIN_EVTQ_SUBCRIBE), tid, evtType);
     98 }
     99 
    100 static inline bool eOsEventUnsubscribe(uint32_t tid, uint32_t evtType)
    101 {
    102     return syscallDo2P(SYSCALL_NO(SYSCALL_DOMAIN_OS, SYSCALL_OS_MAIN, SYSCALL_OS_MAIN_EVENTQ, SYSCALL_OS_MAIN_EVTQ_UNSUBCRIBE), tid, evtType);
    103 }
    104 
    105 static inline bool eOsEnqueueEvt(uint32_t evtType, void *evtData, uint32_t tidOfWhoWillFreeThisEvent) // tidOfWhoWillFreeThisEvent is likely your TID
    106 {
    107     return syscallDo3P(SYSCALL_NO(SYSCALL_DOMAIN_OS, SYSCALL_OS_MAIN, SYSCALL_OS_MAIN_EVENTQ, SYSCALL_OS_MAIN_EVTQ_ENQUEUE), evtType, evtData, tidOfWhoWillFreeThisEvent);
    108 }
    109 
    110 static inline bool eOsEnqueueEvtOrFree(uint32_t evtType, void *evtData, EventFreeF evtFreeF, uint32_t tidOfWhoWillFreeThisEvent) // tidOfWhoWillFreeThisEvent is likely your TID
    111 {
    112     bool success = eOsEnqueueEvt(evtType, evtData, tidOfWhoWillFreeThisEvent);
    113     if (!success && evtFreeF)
    114         evtFreeF(evtData);
    115     return success;
    116 }
    117 
    118 static inline bool eOsEnqueuePrivateEvt(uint32_t evtType, void *evtData, uint32_t tidOfWhoWillFreeThisEvent, uint32_t toTid)
    119 {
    120     return syscallDo4P(SYSCALL_NO(SYSCALL_DOMAIN_OS, SYSCALL_OS_MAIN, SYSCALL_OS_MAIN_EVENTQ, SYSCALL_OS_MAIN_EVTQ_ENQUEUE_PRIVATE), evtType, evtData, tidOfWhoWillFreeThisEvent, toTid);
    121 }
    122 
    123 static inline bool eOsRetainCurrentEvent(TaggedPtr *evtFreeingInfoP)
    124 {
    125     return syscallDo1P(SYSCALL_NO(SYSCALL_DOMAIN_OS, SYSCALL_OS_MAIN, SYSCALL_OS_MAIN_EVENTQ, SYSCALL_OS_MAIN_EVTQ_RETAIN_EVT), evtFreeingInfoP);
    126 }
    127 
    128 static inline bool eOsFreeRetainedEvent(uint32_t evtType, void *evtData, TaggedPtr *evtFreeingInfoP)
    129 {
    130     return syscallDo3P(SYSCALL_NO(SYSCALL_DOMAIN_OS, SYSCALL_OS_MAIN, SYSCALL_OS_MAIN_EVENTQ, SYSCALL_OS_MAIN_EVTQ_FREE_RETAINED), evtType, evtData, evtFreeingInfoP);
    131 }
    132 
    133 static inline void eOsLogvInternal(enum LogLevel level, const char *str, uintptr_t args_list)
    134 {
    135     (void)syscallDo3P(SYSCALL_NO(SYSCALL_DOMAIN_OS, SYSCALL_OS_MAIN, SYSCALL_OS_MAIN_LOGGING, SYSCALL_OS_MAIN_LOG_LOGV), level, str, args_list);
    136 }
    137 
    138 static inline void eOsLogv(enum LogLevel level, const char *str, va_list vl)
    139 {
    140     eOsLogvInternal(level, str, VA_LIST_TO_INTEGER(vl));
    141 }
    142 
    143 static inline void eOsLog(enum LogLevel level, const char *str, ...)
    144 {
    145     va_list vl;
    146 
    147     va_start(vl, str);
    148     eOsLogvInternal(level, str, VA_LIST_TO_INTEGER(vl));
    149     va_end(vl);
    150 }
    151 
    152 static inline const struct SensorInfo* eOsSensorSignalInternalEvt(uint32_t handle, uint32_t intEvtNum, uint32_t value1, uint64_t value2)
    153 {
    154     uint32_t value2_lo = value2;
    155     uint32_t value2_hi = value2 >> 32;
    156 
    157     return (const struct SensorInfo*)syscallDo5P(SYSCALL_NO(SYSCALL_DOMAIN_OS, SYSCALL_OS_MAIN, SYSCALL_OS_MAIN_SENSOR, SYSCALL_OS_MAIN_SENSOR_SIGNAL), handle, intEvtNum, value1, value2_lo, value2_hi);
    158 }
    159 
    160 static inline uint32_t eOsSensorRegister(const struct SensorInfo *si, uint32_t tid, void *cookie, bool initComplete)
    161 {
    162     return syscallDo4P(SYSCALL_NO(SYSCALL_DOMAIN_OS, SYSCALL_OS_MAIN, SYSCALL_OS_MAIN_SENSOR, SYSCALL_OS_MAIN_SENSOR_REG), si, tid, cookie, (int)initComplete);
    163 }
    164 
    165 static inline bool eOsSensorUnregister(uint32_t handle)
    166 {
    167     return syscallDo1P(SYSCALL_NO(SYSCALL_DOMAIN_OS, SYSCALL_OS_MAIN, SYSCALL_OS_MAIN_SENSOR, SYSCALL_OS_MAIN_SENSOR_UNREG), handle);
    168 }
    169 
    170 static inline bool eOsSensorRegisterInitComplete(uint32_t handle)
    171 {
    172     return syscallDo1P(SYSCALL_NO(SYSCALL_DOMAIN_OS, SYSCALL_OS_MAIN, SYSCALL_OS_MAIN_SENSOR, SYSCALL_OS_MAIN_SENSOR_REG_INIT_COMP), handle);
    173 }
    174 
    175 static inline const struct SensorInfo* eOsSensorFind(uint32_t sensorType, uint32_t idx, uint32_t *handleP)
    176 {
    177     return (const struct SensorInfo*)syscallDo3P(SYSCALL_NO(SYSCALL_DOMAIN_OS, SYSCALL_OS_MAIN, SYSCALL_OS_MAIN_SENSOR, SYSCALL_OS_MAIN_SENSOR_FIND), sensorType, idx, handleP);
    178 }
    179 
    180 static inline bool eOsSensorRequest(uint32_t clientId, uint32_t sensorHandle, uint32_t rate, uint64_t latency)
    181 {
    182     uint32_t latency_lo = latency;
    183     uint32_t latency_hi = latency >> 32;
    184 
    185     return syscallDo5P(SYSCALL_NO(SYSCALL_DOMAIN_OS, SYSCALL_OS_MAIN, SYSCALL_OS_MAIN_SENSOR, SYSCALL_OS_MAIN_SENSOR_REQUEST), clientId, sensorHandle, rate, latency_lo, latency_hi);
    186 }
    187 
    188 static inline bool eOsSensorRequestRateChange(uint32_t clientId, uint32_t sensorHandle, uint32_t newRate, uint64_t newLatency)
    189 {
    190     uint32_t newLatency_lo = newLatency;
    191     uint32_t newLatency_hi = newLatency >> 32;
    192 
    193     return syscallDo5P(SYSCALL_NO(SYSCALL_DOMAIN_OS, SYSCALL_OS_MAIN, SYSCALL_OS_MAIN_SENSOR, SYSCALL_OS_MAIN_SENSOR_RATE_CHG), clientId, sensorHandle, newRate, newLatency_lo, newLatency_hi);
    194 }
    195 
    196 static inline bool eOsSensorRelease(uint32_t clientId, uint32_t sensorHandle)
    197 {
    198     return syscallDo2P(SYSCALL_NO(SYSCALL_DOMAIN_OS, SYSCALL_OS_MAIN, SYSCALL_OS_MAIN_SENSOR, SYSCALL_OS_MAIN_SENSOR_RELEASE), clientId, sensorHandle);
    199 }
    200 
    201 static inline bool eOsSensorTriggerOndemand(uint32_t clientId, uint32_t sensorHandle)
    202 {
    203     return syscallDo2P(SYSCALL_NO(SYSCALL_DOMAIN_OS, SYSCALL_OS_MAIN, SYSCALL_OS_MAIN_SENSOR, SYSCALL_OS_MAIN_SENSOR_TRIGGER), clientId, sensorHandle);
    204 }
    205 
    206 static inline uint32_t eOsSensorGetCurRate(uint32_t sensorHandle)
    207 {
    208     return syscallDo1P(SYSCALL_NO(SYSCALL_DOMAIN_OS, SYSCALL_OS_MAIN, SYSCALL_OS_MAIN_SENSOR, SYSCALL_OS_MAIN_SENSOR_GET_RATE), sensorHandle);
    209 }
    210 
    211 static inline uint64_t eOsSensorGetTime(void)
    212 {
    213     uint64_t timeNanos;
    214     syscallDo1P(SYSCALL_NO(SYSCALL_DOMAIN_OS, SYSCALL_OS_MAIN, SYSCALL_OS_MAIN_SENSOR, SYSCALL_OS_MAIN_SENSOR_GET_TIME), &timeNanos);
    215     return timeNanos;
    216 }
    217 
    218 static inline uint64_t eOsTimGetTime(void)
    219 {
    220     uint64_t timeNanos;
    221     syscallDo1P(SYSCALL_NO(SYSCALL_DOMAIN_OS, SYSCALL_OS_MAIN, SYSCALL_OS_MAIN_TIME, SYSCALL_OS_MAIN_TIME_GET_TIME), &timeNanos);
    222     return timeNanos;
    223 }
    224 
    225 static inline uint32_t eOsTimTimerSet(uint64_t length, uint32_t jitterPpm, uint32_t driftPpm, uint32_t tid, void* cookie, bool oneShot)
    226 {
    227     uint32_t lengthLo = length;
    228     uint32_t lengthHi = length >> 32;
    229 
    230     return syscallDoGeneric(SYSCALL_NO(SYSCALL_DOMAIN_OS, SYSCALL_OS_MAIN, SYSCALL_OS_MAIN_TIME, SYSCALL_OS_MAIN_TIME_SET_TIMER), lengthLo, lengthHi, jitterPpm, driftPpm, tid, cookie, (int)oneShot);
    231 }
    232 
    233 static inline bool eOsTimTimerCancel(uint32_t timerId)
    234 {
    235     return syscallDo1P(SYSCALL_NO(SYSCALL_DOMAIN_OS, SYSCALL_OS_MAIN, SYSCALL_OS_MAIN_TIME, SYSCALL_OS_MAIN_TIME_CANCEL_TIMER), timerId);
    236 }
    237 
    238 static inline void* eOsHeapAlloc(uint32_t sz)
    239 {
    240     return (void*)syscallDo1P(SYSCALL_NO(SYSCALL_DOMAIN_OS, SYSCALL_OS_MAIN, SYSCALL_OS_MAIN_HEAP, SYSCALL_OS_MAIN_HEAP_ALLOC), sz);
    241 }
    242 
    243 static inline void eOsHeapFree(void* ptr)
    244 {
    245     (void)syscallDo1P(SYSCALL_NO(SYSCALL_DOMAIN_OS, SYSCALL_OS_MAIN, SYSCALL_OS_MAIN_HEAP, SYSCALL_OS_MAIN_HEAP_FREE), ptr);
    246 }
    247 
    248 static inline struct SlabAllocator* eOsSlabAllocatorNew(uint32_t itemSz, uint32_t itemAlign, uint32_t numItems)
    249 {
    250     return (struct SlabAllocator*)syscallDo3P(SYSCALL_NO(SYSCALL_DOMAIN_OS, SYSCALL_OS_MAIN, SYSCALL_OS_MAIN_SLAB, SYSCALL_OS_MAIN_SLAB_NEW), itemSz, itemAlign, numItems);
    251 }
    252 
    253 static inline void eOsSlabAllocatorDestroy(struct SlabAllocator* allocator)
    254 {
    255     (void)syscallDo1P(SYSCALL_NO(SYSCALL_DOMAIN_OS, SYSCALL_OS_MAIN, SYSCALL_OS_MAIN_SLAB, SYSCALL_OS_MAIN_SLAB_DESTROY), allocator);
    256 }
    257 
    258 static inline void* eOsSlabAllocatorAlloc(struct SlabAllocator* allocator)
    259 {
    260     return (void *)syscallDo1P(SYSCALL_NO(SYSCALL_DOMAIN_OS, SYSCALL_OS_MAIN, SYSCALL_OS_MAIN_SLAB, SYSCALL_OS_MAIN_SLAB_ALLOC), allocator);
    261 }
    262 
    263 static inline void eOsSlabAllocatorFree(struct SlabAllocator* allocator, void* ptrP)
    264 {
    265     (void)syscallDo2P(SYSCALL_NO(SYSCALL_DOMAIN_OS, SYSCALL_OS_MAIN, SYSCALL_OS_MAIN_SLAB, SYSCALL_OS_MAIN_SLAB_FREE), allocator, ptrP);
    266 }
    267 
    268 static inline uint64_t eOsHostGetTime(void)
    269 {
    270     uint64_t timeNanos;
    271     syscallDo1P(SYSCALL_NO(SYSCALL_DOMAIN_OS, SYSCALL_OS_MAIN, SYSCALL_OS_MAIN_HOST, SYSCALL_OS_MAIN_HOST_GET_TIME), &timeNanos);
    272     return timeNanos;
    273 }
    274 
    275 static inline uint64_t eOsRtcGetTime(void)
    276 {
    277     uint64_t timeNanos;
    278     syscallDo1P(SYSCALL_NO(SYSCALL_DOMAIN_OS, SYSCALL_OS_MAIN, SYSCALL_OS_MAIN_RTC, SYSCALL_OS_MAIN_RTC_GET_TIME), &timeNanos);
    279     return timeNanos;
    280 }
    281 
    282 static inline struct Gpio* eOsGpioRequest(uint32_t gpioNum)
    283 {
    284     return (struct Gpio*)syscallDo1P(SYSCALL_NO(SYSCALL_DOMAIN_OS, SYSCALL_OS_DRIVERS, SYSCALL_OS_DRV_GPIO, SYSCALL_OS_DRV_GPIO_REQ), gpioNum);
    285 }
    286 
    287 static inline void eOsGpioRelease(struct Gpio* __restrict gpio)
    288 {
    289     syscallDo1P(SYSCALL_NO(SYSCALL_DOMAIN_OS, SYSCALL_OS_DRIVERS, SYSCALL_OS_DRV_GPIO, SYSCALL_OS_DRV_GPIO_REL), gpio);
    290 }
    291 
    292 static inline void eOsGpioConfigInput(const struct Gpio* __restrict gpio, int32_t gpioSpeed, enum GpioPullMode pull)
    293 {
    294     syscallDo3P(SYSCALL_NO(SYSCALL_DOMAIN_OS, SYSCALL_OS_DRIVERS, SYSCALL_OS_DRV_GPIO, SYSCALL_OS_DRV_GPIO_CFG_IN), gpio, gpioSpeed, pull);
    295 }
    296 
    297 static inline void eOsGpioConfigOutput(const struct Gpio* __restrict gpio, int32_t gpioSpeed, enum GpioPullMode pull, enum GpioOpenDrainMode odrMode, bool value)
    298 {
    299     syscallDo5P(SYSCALL_NO(SYSCALL_DOMAIN_OS, SYSCALL_OS_DRIVERS, SYSCALL_OS_DRV_GPIO, SYSCALL_OS_DRV_GPIO_CFG_OUT), gpio, gpioSpeed, pull, odrMode, value);
    300 }
    301 
    302 static inline void eOsGpioConfigAlt(const struct Gpio* __restrict gpio, int32_t gpioSpeed, enum GpioPullMode pull, enum GpioOpenDrainMode odrMode, uint32_t altFunc)
    303 {
    304     syscallDo5P(SYSCALL_NO(SYSCALL_DOMAIN_OS, SYSCALL_OS_DRIVERS, SYSCALL_OS_DRV_GPIO, SYSCALL_OS_DRV_GPIO_CFG_ALT), gpio, gpioSpeed, pull, odrMode, altFunc);
    305 }
    306 
    307 static inline bool eOsGpioGet(const struct Gpio* __restrict gpio)
    308 {
    309     return !!syscallDo1P(SYSCALL_NO(SYSCALL_DOMAIN_OS, SYSCALL_OS_DRIVERS, SYSCALL_OS_DRV_GPIO, SYSCALL_OS_DRV_GPIO_GET), gpio);
    310 }
    311 
    312 static inline void eOsGpioSet(const struct Gpio* __restrict gpio, bool value)
    313 {
    314     syscallDo2P(SYSCALL_NO(SYSCALL_DOMAIN_OS, SYSCALL_OS_DRIVERS, SYSCALL_OS_DRV_GPIO, SYSCALL_OS_DRV_GPIO_SET), gpio, value);
    315 }
    316 
    317 static inline int eOsI2cMasterRequest(uint32_t busId, uint32_t speedInHz)
    318 {
    319     return syscallDo2P(SYSCALL_NO(SYSCALL_DOMAIN_OS, SYSCALL_OS_DRIVERS, SYSCALL_OS_DRV_I2C_MASTER, SYSCALL_OS_DRV_I2CM_REQ), busId, speedInHz);
    320 }
    321 
    322 static inline int eOsI2cMasterRelease(uint32_t busId)
    323 {
    324     return syscallDo1P(SYSCALL_NO(SYSCALL_DOMAIN_OS, SYSCALL_OS_DRIVERS, SYSCALL_OS_DRV_I2C_MASTER, SYSCALL_OS_DRV_I2CM_REL), busId);
    325 }
    326 
    327 static inline int eOsI2cMasterTxRx(uint32_t busId, uint32_t addr, const void *txBuf, size_t txSize, void *rxBuf, size_t rxSize, uint32_t cbkTid, void *cookie)
    328 {
    329     return syscallDoGeneric(SYSCALL_NO(SYSCALL_DOMAIN_OS, SYSCALL_OS_DRIVERS, SYSCALL_OS_DRV_I2C_MASTER, SYSCALL_OS_DRV_I2CM_TXRX), busId, addr, txBuf, txSize, rxBuf, rxSize, cbkTid, cookie);
    330 }
    331 
    332 static inline int eOsI2cSlaveRequest(uint32_t busId, uint32_t addr)
    333 {
    334     return syscallDo2P(SYSCALL_NO(SYSCALL_DOMAIN_OS, SYSCALL_OS_DRIVERS, SYSCALL_OS_DRV_I2C_SLAVE, SYSCALL_OS_DRV_I2CS_REQ), busId, addr);
    335 }
    336 
    337 static inline int eOsI2cSlaveRelease(uint32_t busId)
    338 {
    339     return syscallDo1P(SYSCALL_NO(SYSCALL_DOMAIN_OS, SYSCALL_OS_DRIVERS, SYSCALL_OS_DRV_I2C_SLAVE, SYSCALL_OS_DRV_I2CS_REL), busId);
    340 }
    341 
    342 static inline void eOsI2cSlaveEnableRx(uint32_t busId, void *rxBuf, size_t rxSize, uint32_t cbkTid, void *cookie)
    343 {
    344     syscallDo5P(SYSCALL_NO(SYSCALL_DOMAIN_OS, SYSCALL_OS_DRIVERS, SYSCALL_OS_DRV_I2C_SLAVE, SYSCALL_OS_DRV_I2CS_RX_EN), busId, rxBuf, rxSize, cbkTid, cookie);
    345 }
    346 
    347 static inline int eOsI2cSlaveTxPreamble(uint32_t busId, uint8_t byte, uint32_t cbkTid, void *cookie)
    348 {
    349     return syscallDo4P(SYSCALL_NO(SYSCALL_DOMAIN_OS, SYSCALL_OS_DRIVERS, SYSCALL_OS_DRV_I2C_SLAVE, SYSCALL_OS_DRV_I2CS_TX_PRE), busId, byte, cbkTid, cookie);
    350 }
    351 
    352 static inline int eOsI2cSlaveTxPacket(uint32_t busId, const void *txBuf, size_t txSize, uint32_t cbkTid, void *cookie)
    353 {
    354     return syscallDo5P(SYSCALL_NO(SYSCALL_DOMAIN_OS, SYSCALL_OS_DRIVERS, SYSCALL_OS_DRV_I2C_SLAVE, SYSCALL_OS_DRV_I2CS_TX_PKT), busId, txBuf, txSize, cbkTid, cookie);
    355 }
    356 
    357 #ifdef __cplusplus
    358 }
    359 #endif
    360 
    361 #endif
    362 
    363