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 _NANOHUB_SYSTEM_COMMS_H_
18#define _NANOHUB_SYSTEM_COMMS_H_
19
20#include <utils/Condition.h>
21
22#include <condition_variable>
23#include <map>
24#include <mutex>
25#include <vector>
26
27#include <hardware/context_hub.h>
28#include <nanohub/nanohub.h>
29
30#include "nanohubhal.h"
31#include "message_buf.h"
32
33//rx: return 0 if handled, > 0 if not handled, < 0 if error happened
34
35#define MSG_HANDLED 0
36
37//messages to the HostIf nanoapp & their replies (mesages and replies both begin with u8 message_type)
38#define NANOHUB_EXT_APPS_ON        0 // () -> (char success)
39#define NANOHUB_EXT_APPS_OFF       1 // () -> (char success)
40#define NANOHUB_EXT_APP_DELETE     2 // (u64 name) -> (char success)    //idempotent
41#define NANOHUB_QUERY_MEMINFO      3 // () -> (mem_info)
42#define NANOHUB_QUERY_APPS         4 // (u32 idxStart) -> (app_info[idxStart] OR EMPTY IF NO MORE)
43#define NANOHUB_QUERY_RSA_KEYS     5 // (u32 byteOffset) -> (u8 data[1 or more bytes] OR EMPTY IF NO MORE)
44#define NANOHUB_START_UPLOAD       6 // (char isOs, u32 totalLenToTx) -> (char success)
45#define NANOHUB_CONT_UPLOAD        7 // (u32 offset, u8 data[]) -> (char success)
46#define NANOHUB_FINISH_UPLOAD      8 // () -> (char success)
47#define NANOHUB_REBOOT             9 // () -> (char success)
48
49#define NANOHUB_APP_NOT_LOADED  (-1)
50#define NANOHUB_APP_LOADED      (0)
51
52#define NANOHUB_UPLOAD_CHUNK_SZ_MAX 64
53#define NANOHUB_MEM_SZ_UNKNOWN      0xFFFFFFFFUL
54
55namespace android {
56
57namespace nanohub {
58
59int system_comms_handle_rx(const nano_message *msg);
60int system_comms_handle_tx(const hub_message_t *outMsg);
61
62struct NanohubAppInfo {
63    hub_app_name_t name;
64    uint32_t version, flashUse, ramUse;
65} __attribute__((packed));
66
67struct MgmtStatus {
68    union {
69        uint32_t value;
70        struct {
71            uint8_t app;
72            uint8_t task;
73            uint8_t op;
74            uint8_t erase;
75        } __attribute__((packed));
76    };
77} __attribute__((packed));
78
79struct NanohubMemInfo {
80    //sizes
81    uint32_t flashSz, blSz, osSz, sharedSz, eeSz;
82    uint32_t ramSz;
83
84    //use
85    uint32_t blUse, osUse, sharedUse, eeUse;
86    uint32_t ramUse;
87} __attribute__((packed));
88
89struct NanohubRsp {
90    uint32_t cmd;
91    int32_t status;
92    explicit NanohubRsp(MessageBuf &buf, bool no_status = false);
93};
94
95inline bool operator == (const hub_app_name_t &a, const hub_app_name_t &b) {
96    return a.id == b.id;
97}
98
99inline bool operator != (const hub_app_name_t &a, const hub_app_name_t &b) {
100    return !(a == b);
101}
102
103class SystemComm {
104private:
105
106    /*
107     * Nanohub HAL sessions
108     *
109     * Session is an object that can group several message exchanges with FW,
110     * maintain state, and be waited for completion by someone else.
111     *
112     * As of this moment, since all sessions are triggered by client thread,
113     * and all the exchange is happening in local worker thread, it is only possible
114     * for client thread to wait on session completion.
115     * Allowing sessions to wait on each other will require a worker thread pool.
116     * It is now unnecessary, and not implemented.
117     */
118    class ISession {
119    public:
120        virtual int setup(const hub_message_t *app_msg) = 0;
121        virtual int handleRx(MessageBuf &buf) = 0;
122        virtual int getState() const = 0; // FSM state
123        virtual int getStatus() const = 0; // execution status (result code)
124        virtual void abort(int32_t) = 0;
125        virtual ~ISession() {}
126    };
127
128    class SessionManager;
129
130    class Session : public ISession {
131        friend class SessionManager;
132
133        mutable std::mutex mDoneMutex; // controls condition and state transitions
134        std::condition_variable mDoneCond;
135        volatile int mState;
136
137    protected:
138        mutable std::mutex mLock; // serializes message handling
139        int32_t mStatus;
140
141        enum {
142            SESSION_INIT = 0,
143            SESSION_DONE = 1,
144            SESSION_USER = 2,
145        };
146
147        void complete() {
148            std::unique_lock<std::mutex> lk(mDoneMutex);
149            if (mState != SESSION_DONE) {
150                mState = SESSION_DONE;
151                lk.unlock();
152                mDoneCond.notify_all();
153            }
154        }
155        void abort(int32_t status) {
156            std::lock_guard<std::mutex> _l(mLock);
157            mStatus = status;
158            complete();
159        }
160        void setState(int state) {
161            if (state == SESSION_DONE) {
162                complete();
163            } else {
164                std::lock_guard<std::mutex> _l(mDoneMutex);
165                mState = state;
166            }
167        }
168    public:
169        Session() { mState = SESSION_INIT; mStatus = -1; }
170        int getStatus() const {
171            std::lock_guard<std::mutex> _l(mLock);
172            return mStatus;
173        }
174        int wait() {
175            std::unique_lock<std::mutex> lk(mDoneMutex);
176            mDoneCond.wait(lk, [this] { return mState == SESSION_DONE; });
177            return 0;
178        }
179        virtual int getState() const override {
180            std::lock_guard<std::mutex> _l(mDoneMutex);
181            return mState;
182        }
183        virtual bool isDone() const {
184            std::lock_guard<std::mutex> _l(mDoneMutex);
185            return mState == SESSION_DONE;
186        }
187        virtual bool isRunning() const {
188            std::lock_guard<std::mutex> _l(mDoneMutex);
189            return mState > SESSION_DONE;
190        }
191    };
192
193    class AppMgmtSession : public Session {
194        enum {
195            TRANSFER = SESSION_USER,
196            FINISH,
197            RUN,
198            RUN_FAILED,
199            REBOOT,
200            MGMT,
201        };
202        uint32_t mCmd; // LOAD_APP, UNLOAD_APP, ENABLE_APP, DISABLE_APP
203        uint32_t mResult;
204        std::vector<uint8_t> mData;
205        uint32_t mLen;
206        uint32_t mPos;
207        hub_app_name_t mAppName;
208
209        int setupMgmt(const hub_message_t *appMsg, uint32_t cmd);
210        int handleTransfer(NanohubRsp &rsp);
211        int handleFinish(NanohubRsp &rsp);
212        int handleRun(NanohubRsp &rsp);
213        int handleRunFailed(NanohubRsp &rsp);
214        int handleReboot(NanohubRsp &rsp);
215        int handleMgmt(NanohubRsp &rsp);
216    public:
217        AppMgmtSession() {
218            mCmd = 0;
219            mResult = 0;
220            mPos = 0;
221            mLen = 0;
222            memset(&mAppName, 0, sizeof(mAppName));
223        }
224        virtual int handleRx(MessageBuf &buf) override;
225        virtual int setup(const hub_message_t *app_msg) override;
226    };
227
228    class MemInfoSession : public Session {
229    public:
230        virtual int setup(const hub_message_t *app_msg) override;
231        virtual int handleRx(MessageBuf &buf) override;
232    };
233
234    class KeyInfoSession  : public Session {
235        std::vector<uint8_t> mRsaKeyData;
236        int requestRsaKeys(void);
237    public:
238        virtual int setup(const hub_message_t *) override;
239        virtual int handleRx(MessageBuf &buf) override;
240        bool haveKeys() const {
241            std::lock_guard<std::mutex> _l(mLock);
242            return mRsaKeyData.size() > 0 && !isRunning();
243        }
244    };
245
246    class AppInfoSession : public Session {
247        std::vector<hub_app_info> mAppInfo;
248        int requestNext();
249    public:
250        virtual int setup(const hub_message_t *) override;
251        virtual int handleRx(MessageBuf &buf) override;
252    };
253
254    class SessionManager {
255        typedef std::map<int, Session* > SessionMap;
256
257        std::mutex lock;
258        SessionMap sessions_;
259
260        bool isActive(const SessionMap::iterator &pos) const
261        {
262            return !pos->second->isDone();
263        }
264        void next(SessionMap::iterator &pos)
265        {
266            isActive(pos) ? pos++ : pos = sessions_.erase(pos);
267        }
268
269    public:
270        int handleRx(MessageBuf &buf);
271        int setup_and_add(int id, Session *session, const hub_message_t *appMsg);
272    } mSessions;
273
274    const hub_app_name_t mHostIfAppName = {
275        .id = APP_ID_MAKE(NANOHUB_VENDOR_GOOGLE, 0)
276    };
277
278    static SystemComm *getSystem() {
279        // this is thread-safe in c++11
280        static SystemComm theInstance;
281        return &theInstance;
282    }
283
284    SystemComm () = default;
285    ~SystemComm() = default;
286
287    int doHandleTx(const hub_message_t *txMsg);
288    int doHandleRx(const nano_message *rxMsg);
289
290    static void sendToApp(uint32_t typ, const void *data, uint32_t len) {
291        if (NanoHub::messageTracingEnabled()) {
292            dumpBuffer("HAL -> APP", get_hub_info()->os_app_name, typ, data, len);
293        }
294        NanoHub::sendToApp(HubMessage(&get_hub_info()->os_app_name, typ, data, len));
295    }
296    static int sendToSystem(const void *data, size_t len);
297
298    KeyInfoSession mKeySession;
299    AppMgmtSession mAppMgmtSession;
300    AppInfoSession mAppInfoSession;
301    MemInfoSession mMemInfoSession;
302
303public:
304    static int handleTx(const hub_message_t *txMsg) {
305        return getSystem()->doHandleTx(txMsg);
306    }
307    static int handleRx(const nano_message *rxMsg) {
308        return getSystem()->doHandleRx(rxMsg);
309    }
310};
311
312}; // namespace nanohub
313
314}; // namespace android
315
316#endif
317