Home | History | Annotate | Download | only in nanoapp_cmd
      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 #define LOG_TAG "nanoapp_cmd"
     18 
     19 #include <assert.h>
     20 #include <errno.h>
     21 #include <fcntl.h>
     22 #include <inttypes.h>
     23 #include <signal.h>
     24 #include <stdbool.h>
     25 #include <stdint.h>
     26 #include <stdio.h>
     27 #include <stdlib.h>
     28 #include <sys/stat.h>
     29 #include <sys/types.h>
     30 #include <string.h>
     31 #include <unistd.h>
     32 
     33 #include <android/log.h>
     34 
     35 #include <nanohub/nanohub.h>
     36 #include <eventnums.h>
     37 #include <sensType.h>
     38 
     39 #define SENSOR_RATE_ONCHANGE    0xFFFFFF01UL
     40 #define SENSOR_RATE_ONESHOT     0xFFFFFF02UL
     41 #define SENSOR_HZ(_hz)          ((uint32_t)((_hz) * 1024.0f))
     42 #define MAX_APP_NAME_LEN        32
     43 #define MAX_INSTALL_CNT         8
     44 #define MAX_UNINSTALL_CNT       8
     45 #define MAX_DOWNLOAD_RETRIES    4
     46 #define UNINSTALL_CMD           "uninstall"
     47 
     48 #define NANOHUB_HAL_EXT_APPS_ON     0
     49 #define NANOHUB_HAL_EXT_APPS_OFF    1
     50 #define NANOHUB_HAL_EXT_APP_DELETE  2
     51 
     52 #define LOGE(fmt, ...) do { \
     53         __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, fmt, ##__VA_ARGS__); \
     54         printf(fmt "\n", ##__VA_ARGS__); \
     55     } while (0)
     56 
     57 enum ConfigCmds
     58 {
     59     CONFIG_CMD_DISABLE      = 0,
     60     CONFIG_CMD_ENABLE       = 1,
     61     CONFIG_CMD_FLUSH        = 2,
     62     CONFIG_CMD_CFG_DATA     = 3,
     63     CONFIG_CMD_CALIBRATE    = 4,
     64 };
     65 
     66 struct ConfigCmd
     67 {
     68     uint32_t evtType;
     69     uint64_t latency;
     70     uint32_t rate;
     71     uint8_t sensorType;
     72     uint8_t cmd;
     73     uint16_t flags;
     74     uint8_t data[];
     75 } __attribute__((packed));
     76 
     77 struct HalCmd
     78 {
     79     struct HostMsgHdr hdr;
     80     uint8_t cmd;
     81     uint64_t appId;
     82 } __attribute__((packed));
     83 
     84 struct App
     85 {
     86     uint32_t num;
     87     uint64_t id;
     88     uint32_t version;
     89     uint32_t size;
     90 };
     91 
     92 struct LedsCfg {
     93     uint32_t led_num;
     94     uint32_t value;
     95 } __attribute__((packed));
     96 
     97 static int setType(struct ConfigCmd *cmd, char *sensor)
     98 {
     99     if (strcmp(sensor, "accel") == 0) {
    100         cmd->sensorType = SENS_TYPE_ACCEL;
    101     } else if (strcmp(sensor, "gyro") == 0) {
    102         cmd->sensorType = SENS_TYPE_GYRO;
    103     } else if (strcmp(sensor, "mag") == 0) {
    104         cmd->sensorType = SENS_TYPE_MAG;
    105     } else if (strcmp(sensor, "uncal_accel") == 0) {
    106         cmd->sensorType = SENS_TYPE_ACCEL;
    107     } else if (strcmp(sensor, "uncal_gyro") == 0) {
    108         cmd->sensorType = SENS_TYPE_GYRO;
    109     } else if (strcmp(sensor, "uncal_mag") == 0) {
    110         cmd->sensorType = SENS_TYPE_MAG;
    111     } else if (strcmp(sensor, "als") == 0) {
    112         cmd->sensorType = SENS_TYPE_ALS;
    113     } else if (strcmp(sensor, "prox") == 0) {
    114         cmd->sensorType = SENS_TYPE_PROX;
    115     } else if (strcmp(sensor, "baro") == 0) {
    116         cmd->sensorType = SENS_TYPE_BARO;
    117     } else if (strcmp(sensor, "temp") == 0) {
    118         cmd->sensorType = SENS_TYPE_TEMP;
    119     } else if (strcmp(sensor, "ambient_temp") == 0) {
    120         cmd->sensorType = SENS_TYPE_AMBIENT_TEMP;
    121     } else if (strcmp(sensor, "orien") == 0) {
    122         cmd->sensorType = SENS_TYPE_ORIENTATION;
    123     } else if (strcmp(sensor, "gravity") == 0) {
    124         cmd->sensorType = SENS_TYPE_GRAVITY;
    125     } else if (strcmp(sensor, "geomag") == 0) {
    126         cmd->sensorType = SENS_TYPE_GEO_MAG_ROT_VEC;
    127     } else if (strcmp(sensor, "linear_acc") == 0) {
    128         cmd->sensorType = SENS_TYPE_LINEAR_ACCEL;
    129     } else if (strcmp(sensor, "rotation") == 0) {
    130         cmd->sensorType = SENS_TYPE_ROTATION_VECTOR;
    131     } else if (strcmp(sensor, "game") == 0) {
    132         cmd->sensorType = SENS_TYPE_GAME_ROT_VECTOR;
    133     } else if (strcmp(sensor, "win_orien") == 0) {
    134         cmd->sensorType = SENS_TYPE_WIN_ORIENTATION;
    135         cmd->rate = SENSOR_RATE_ONCHANGE;
    136     } else if (strcmp(sensor, "tilt") == 0) {
    137         cmd->sensorType = SENS_TYPE_TILT;
    138         cmd->rate = SENSOR_RATE_ONCHANGE;
    139     } else if (strcmp(sensor, "step_det") == 0) {
    140         cmd->sensorType = SENS_TYPE_STEP_DETECT;
    141         cmd->rate = SENSOR_RATE_ONCHANGE;
    142     } else if (strcmp(sensor, "step_cnt") == 0) {
    143         cmd->sensorType = SENS_TYPE_STEP_COUNT;
    144         cmd->rate = SENSOR_RATE_ONCHANGE;
    145     } else if (strcmp(sensor, "double_tap") == 0) {
    146         cmd->sensorType = SENS_TYPE_DOUBLE_TAP;
    147         cmd->rate = SENSOR_RATE_ONCHANGE;
    148     } else if (strcmp(sensor, "flat") == 0) {
    149         cmd->sensorType = SENS_TYPE_FLAT;
    150         cmd->rate = SENSOR_RATE_ONCHANGE;
    151     } else if (strcmp(sensor, "anymo") == 0) {
    152         cmd->sensorType = SENS_TYPE_ANY_MOTION;
    153         cmd->rate = SENSOR_RATE_ONCHANGE;
    154     } else if (strcmp(sensor, "nomo") == 0) {
    155         cmd->sensorType = SENS_TYPE_NO_MOTION;
    156         cmd->rate = SENSOR_RATE_ONCHANGE;
    157     } else if (strcmp(sensor, "sigmo") == 0) {
    158         cmd->sensorType = SENS_TYPE_SIG_MOTION;
    159         cmd->rate = SENSOR_RATE_ONESHOT;
    160     } else if (strcmp(sensor, "gesture") == 0) {
    161         cmd->sensorType = SENS_TYPE_GESTURE;
    162         cmd->rate = SENSOR_RATE_ONESHOT;
    163     } else if (strcmp(sensor, "hall") == 0) {
    164         cmd->sensorType = SENS_TYPE_HALL;
    165         cmd->rate = SENSOR_RATE_ONCHANGE;
    166     } else if (strcmp(sensor, "vsync") == 0) {
    167         cmd->sensorType = SENS_TYPE_VSYNC;
    168         cmd->rate = SENSOR_RATE_ONCHANGE;
    169     } else if (strcmp(sensor, "activity") == 0) {
    170         cmd->sensorType = SENS_TYPE_ACTIVITY;
    171         cmd->rate = SENSOR_RATE_ONCHANGE;
    172     } else if (strcmp(sensor, "twist") == 0) {
    173         cmd->sensorType = SENS_TYPE_DOUBLE_TWIST;
    174         cmd->rate = SENSOR_RATE_ONCHANGE;
    175     } else if (strcmp(sensor, "leds") == 0) {
    176         cmd->sensorType = SENS_TYPE_LEDS;
    177     } else if (strcmp(sensor, "leds_i2c") == 0) {
    178         cmd->sensorType = SENS_TYPE_LEDS_I2C;
    179     } else if (strcmp(sensor, "humidity") == 0) {
    180         cmd->sensorType = SENS_TYPE_HUMIDITY;
    181     } else {
    182         return 1;
    183     }
    184 
    185     return 0;
    186 }
    187 
    188 bool drain = false;
    189 bool stop = false;
    190 char *buf;
    191 int nread, buf_size = 2048;
    192 struct App apps[32];
    193 uint8_t appCount;
    194 char appsToInstall[MAX_INSTALL_CNT][MAX_APP_NAME_LEN+1];
    195 uint64_t appsToUninstall[MAX_UNINSTALL_CNT];
    196 
    197 void sig_handle(__attribute__((unused)) int sig)
    198 {
    199     assert(sig == SIGINT);
    200     printf("Terminating...\n");
    201     stop = true;
    202 }
    203 
    204 FILE *openFile(const char *fname, const char *mode)
    205 {
    206     FILE *f = fopen(fname, mode);
    207     if (f == NULL) {
    208         LOGE("Failed to open %s: err=%d [%s]", fname, errno, strerror(errno));
    209     }
    210     return f;
    211 }
    212 
    213 void parseInstalledAppInfo()
    214 {
    215     FILE *fp;
    216     char *line = NULL;
    217     size_t len;
    218     ssize_t numRead;
    219 
    220     appCount = 0;
    221 
    222     fp = openFile("/sys/class/nanohub/nanohub/app_info", "r");
    223     if (!fp)
    224         return;
    225 
    226     while ((numRead = getline(&line, &len, fp)) != -1) {
    227         struct App *currApp = &apps[appCount++];
    228         sscanf(line, "app: %d id: %" PRIx64 " ver: %" PRIx32 " size: %" PRIx32 "\n", &currApp->num, &currApp->id, &currApp->version, &currApp->size);
    229     }
    230 
    231     fclose(fp);
    232 
    233     if (line)
    234         free(line);
    235 }
    236 
    237 struct App *findApp(uint64_t appId)
    238 {
    239     uint8_t i;
    240 
    241     for (i = 0; i < appCount; i++) {
    242         if (apps[i].id == appId) {
    243             return &apps[i];
    244         }
    245     }
    246 
    247     return NULL;
    248 }
    249 
    250 int findAppIdByName(char *name, uint64_t *appId)
    251 {
    252     FILE *fp;
    253     char *line = NULL;
    254     size_t len;
    255     ssize_t numRead;
    256     int ret = 0;
    257 
    258     fp = openFile("/vendor/firmware/napp_list.cfg", "r");
    259     if (!fp)
    260         return -1;
    261 
    262     while ((numRead = getline(&line, &len, fp)) != -1) {
    263         char entry[MAX_APP_NAME_LEN+1];
    264         uint32_t appVersion;
    265 
    266         sscanf(line, "%" STRINGIFY(MAX_APP_NAME_LEN) "s %" PRIx64 " %" PRIx32 "\n", entry, appId, &appVersion);
    267 
    268         if (strncmp(entry, name, MAX_APP_NAME_LEN) == 0) {
    269             ret = 1;
    270             break;
    271         }
    272     }
    273 
    274     fclose(fp);
    275 
    276     if (line)
    277         free(line);
    278 
    279     return ret;
    280 }
    281 
    282 int parseConfigAppInfo(int *installCnt, int *uninstallCnt)
    283 {
    284     FILE *fp;
    285     char *line = NULL;
    286     size_t len;
    287     ssize_t numRead;
    288 
    289     fp = openFile("/vendor/firmware/napp_list.cfg", "r");
    290     if (!fp)
    291         return -1;
    292 
    293     parseInstalledAppInfo();
    294 
    295     *installCnt = *uninstallCnt = 0;
    296     while (((numRead = getline(&line, &len, fp)) != -1) && (*installCnt < MAX_INSTALL_CNT) && (*uninstallCnt < MAX_UNINSTALL_CNT)) {
    297         uint64_t appId;
    298         uint32_t appVersion;
    299         struct App *installedApp;
    300 
    301         sscanf(line, "%" STRINGIFY(MAX_APP_NAME_LEN) "s %" PRIx64 " %" PRIx32 "\n", appsToInstall[*installCnt], &appId, &appVersion);
    302 
    303         installedApp = findApp(appId);
    304         if (strncmp(appsToInstall[*installCnt], UNINSTALL_CMD, MAX_APP_NAME_LEN) == 0) {
    305             if (installedApp) {
    306                 appsToUninstall[*uninstallCnt] = appId;
    307                 (*uninstallCnt)++;
    308             }
    309         } else if (!installedApp || (installedApp->version < appVersion)) {
    310             (*installCnt)++;
    311         }
    312     }
    313 
    314     fclose(fp);
    315 
    316     if (line)
    317         free(line);
    318 
    319     return *installCnt + *uninstallCnt;
    320 }
    321 
    322 bool fileWriteData(const char *fname, const void *data, size_t size)
    323 {
    324     int fd;
    325     bool result;
    326 
    327     fd = open(fname, O_WRONLY);
    328     if (fd < 0) {
    329         LOGE("Failed to open %s: err=%d [%s]", fname, errno, strerror(errno));
    330         return false;
    331     }
    332 
    333     result = true;
    334     if ((size_t)write(fd, data, size) != size) {
    335         LOGE("Failed to write to %s; err=%d [%s]", fname, errno, strerror(errno));
    336         result = false;
    337     }
    338     close(fd);
    339 
    340     return result;
    341 }
    342 
    343 void downloadNanohub()
    344 {
    345     char c = '1';
    346 
    347     printf("Updating nanohub OS [if required]...");
    348     fflush(stdout);
    349     if (fileWriteData("/sys/class/nanohub/nanohub/download_bl", &c, sizeof(c)))
    350         printf("done\n");
    351 }
    352 
    353 bool sendCmd(uint8_t cmd, uint64_t appId)
    354 {
    355     struct HalCmd msg;
    356 
    357     msg.hdr.eventId = EVT_APP_FROM_HOST;
    358     msg.hdr.appId = APP_ID_MAKE(NANOHUB_VENDOR_GOOGLE, 0);
    359     msg.hdr.len = sizeof(msg) - sizeof(msg.hdr); // payload length
    360     msg.cmd = cmd;
    361     memcpy(&msg.appId, &appId, sizeof(uint64_t));
    362 
    363     return fileWriteData("/dev/nanohub", &msg, sizeof(msg));
    364 }
    365 
    366 int halCmd(uint8_t cmd, char *arg)
    367 {
    368     uint64_t appId;
    369     char *endptr = arg + strlen(arg);
    370 
    371     if (strcmp(arg, UNINSTALL_CMD) == 0) {
    372         printf("%s is not a valid app name\n", arg);
    373         return 1;
    374     }
    375 
    376     if ((findAppIdByName(arg, &appId) == 1) || (appId = strtoull(arg, &endptr, 16)) > 0) {
    377         if (*endptr != '\0') {
    378             printf("Couldn't find nanoapp '%s' in napp_list.cfg\n", arg);
    379             return 1;
    380         } else if (cmd == NANOHUB_HAL_EXT_APPS_ON)
    381             printf("Loading ");
    382         else if (cmd == NANOHUB_HAL_EXT_APPS_OFF)
    383             printf("Unloading ");
    384         else if (cmd == NANOHUB_HAL_EXT_APP_DELETE)
    385             printf("Deleting ");
    386         else {
    387             printf("Unrecognized cmd: %d\n", cmd);
    388             return 1;
    389         }
    390         printf("\"0x%016" PRIx64 "\"...", appId);
    391         fflush(stdout);
    392         if (sendCmd(cmd, appId))
    393             printf("done\n");
    394         return 0;
    395     } else {
    396         printf("Couldn't find nanoapp '%s' in napp_list.cfg\n", arg);
    397         return 1;
    398     }
    399 }
    400 
    401 void removeApps(int updateCnt)
    402 {
    403     int i;
    404 
    405     for (i = 0; i < updateCnt; i++) {
    406         printf("Deleting \"0x%016" PRIx64 "\"...", appsToUninstall[i]);
    407         fflush(stdout);
    408         if (sendCmd(NANOHUB_HAL_EXT_APP_DELETE, appsToUninstall[i]))
    409             printf("done\n");
    410     }
    411 }
    412 
    413 void downloadApps(int updateCnt)
    414 {
    415     int i;
    416 
    417     for (i = 0; i < updateCnt; i++) {
    418         printf("Downloading \"%s.napp\"...", appsToInstall[i]);
    419         fflush(stdout);
    420         if (fileWriteData("/sys/class/nanohub/nanohub/download_app", appsToInstall[i], strlen(appsToInstall[i])))
    421             printf("done\n");
    422     }
    423 }
    424 
    425 void eraseSharedArea()
    426 {
    427     char c = '1';
    428 
    429     printf("Erasing entire nanohub shared area...");
    430     fflush(stdout);
    431     if (fileWriteData("/sys/class/nanohub/nanohub/erase_shared", &c, sizeof(c)))
    432         printf("done\n");
    433 }
    434 
    435 void resetHub()
    436 {
    437     char c = '1';
    438 
    439     printf("Resetting nanohub...");
    440     fflush(stdout);
    441     if (fileWriteData("/sys/class/nanohub/nanohub/reset", &c, sizeof(c)))
    442         printf("done\n");
    443 }
    444 
    445 int main(int argc, char *argv[])
    446 {
    447     struct ConfigCmd mConfigCmd;
    448     struct ConfigCmd *pConfigCmd = &mConfigCmd;
    449     size_t length = sizeof(mConfigCmd);
    450     int fd;
    451     int i;
    452 
    453     if (argc < 3 && (argc < 2 || strcmp(argv[1], "download") != 0)) {
    454         printf("usage: %s <action> <sensor> <data> -d\n", argv[0]);
    455         printf("       action: config|cfgdata|calibrate|flush\n");
    456         printf("       sensor: (uncal_)accel|(uncal_)gyro|(uncal_)mag|als|prox|baro|temp|orien\n");
    457         printf("               gravity|geomag|linear_acc|rotation|game\n");
    458         printf("               win_orien|tilt|step_det|step_cnt|double_tap\n");
    459         printf("               flat|anymo|nomo|sigmo|gesture|hall|vsync\n");
    460         printf("               activity|twist|leds|leds_i2c|humidity|ambient_temp\n");
    461         printf("       data: config: <true|false> <rate in Hz> <latency in u-sec>\n");
    462         printf("             cfgdata: leds: led_num value\n");
    463         printf("             calibrate: [N.A.]\n");
    464         printf("             flush: [N.A.]\n");
    465         printf("       -d: if specified, %s will keep draining /dev/nanohub until cancelled.\n", argv[0]);
    466         printf("usage: %s <cmd> [app]\n", argv[0]);
    467         printf("       cmd: download|load|unload|delete\n");
    468         printf("       app: appId or name from napp_list.cfg\n");
    469 
    470         return 1;
    471     }
    472 
    473     if (strcmp(argv[1], "config") == 0) {
    474         if (argc != 6 && argc != 7) {
    475             printf("Wrong arg number\n");
    476             return 1;
    477         }
    478         if (argc == 7) {
    479             if(strcmp(argv[6], "-d") == 0) {
    480                 drain = true;
    481             } else {
    482                 printf("Last arg unsupported, ignored.\n");
    483             }
    484         }
    485         if (strcmp(argv[3], "true") == 0)
    486             mConfigCmd.cmd = CONFIG_CMD_ENABLE;
    487         else if (strcmp(argv[3], "false") == 0) {
    488             mConfigCmd.cmd = CONFIG_CMD_DISABLE;
    489         } else {
    490             printf("Unsupported data: %s For action: %s\n", argv[3], argv[1]);
    491             return 1;
    492         }
    493         mConfigCmd.evtType = EVT_NO_SENSOR_CONFIG_EVENT;
    494         mConfigCmd.rate = SENSOR_HZ((float)atoi(argv[4]));
    495         mConfigCmd.latency = atoi(argv[5]) * 1000ull;
    496         if (setType(&mConfigCmd, argv[2])) {
    497             printf("Unsupported sensor: %s For action: %s\n", argv[2], argv[1]);
    498             return 1;
    499         }
    500     } else if (strcmp(argv[1], "cfgdata") == 0) {
    501         mConfigCmd.evtType = EVT_NO_SENSOR_CONFIG_EVENT;
    502         mConfigCmd.rate = 0;
    503         mConfigCmd.latency = 0;
    504         mConfigCmd.cmd = CONFIG_CMD_CFG_DATA;
    505         if (setType(&mConfigCmd, argv[2])) {
    506             printf("Unsupported sensor: %s For action: %s\n", argv[2], argv[1]);
    507             return 1;
    508         }
    509         if (mConfigCmd.sensorType == SENS_TYPE_LEDS ||
    510             mConfigCmd.sensorType == SENS_TYPE_LEDS_I2C) {
    511             struct LedsCfg mLedsCfg;
    512 
    513             if (argc != 5) {
    514                 printf("Wrong arg number\n");
    515                 return 1;
    516             }
    517             length = sizeof(struct ConfigCmd) + sizeof(struct LedsCfg *);
    518             pConfigCmd = (struct ConfigCmd *)malloc(length);
    519             if (!pConfigCmd)
    520                 return 1;
    521             mLedsCfg.led_num = atoi(argv[3]);
    522             mLedsCfg.value = atoi(argv[4]);
    523             memcpy(pConfigCmd, &mConfigCmd, sizeof(mConfigCmd));
    524             memcpy(pConfigCmd->data, &mLedsCfg, sizeof(mLedsCfg));
    525         } else {
    526             printf("Unsupported sensor: %s For action: %s\n", argv[2], argv[1]);
    527             return 1;
    528         }
    529     } else if (strcmp(argv[1], "calibrate") == 0) {
    530         if (argc != 3) {
    531             printf("Wrong arg number\n");
    532             return 1;
    533         }
    534         mConfigCmd.evtType = EVT_NO_SENSOR_CONFIG_EVENT;
    535         mConfigCmd.rate = 0;
    536         mConfigCmd.latency = 0;
    537         mConfigCmd.cmd = CONFIG_CMD_CALIBRATE;
    538         if (setType(&mConfigCmd, argv[2])) {
    539             printf("Unsupported sensor: %s For action: %s\n", argv[2], argv[1]);
    540             return 1;
    541         }
    542     } else if (strcmp(argv[1], "flush") == 0) {
    543         if (argc != 3) {
    544             printf("Wrong arg number\n");
    545             return 1;
    546         }
    547         mConfigCmd.evtType = EVT_NO_SENSOR_CONFIG_EVENT;
    548         mConfigCmd.rate = 0;
    549         mConfigCmd.latency = 0;
    550         mConfigCmd.cmd = CONFIG_CMD_FLUSH;
    551         if (setType(&mConfigCmd, argv[2])) {
    552             printf("Unsupported sensor: %s For action: %s\n", argv[2], argv[1]);
    553             return 1;
    554         }
    555     } else if (strcmp(argv[1], "load") == 0) {
    556         if (argc != 3) {
    557             printf("Wrong arg number\n");
    558             return 1;
    559         }
    560 
    561         return halCmd(NANOHUB_HAL_EXT_APPS_ON, argv[2]);
    562     } else if (strcmp(argv[1], "unload") == 0) {
    563         if (argc != 3) {
    564             printf("Wrong arg number\n");
    565             return 1;
    566         }
    567 
    568         return halCmd(NANOHUB_HAL_EXT_APPS_OFF, argv[2]);
    569     } else if (strcmp(argv[1], "delete") == 0) {
    570         if (argc != 3) {
    571             printf("Wrong arg number\n");
    572             return 1;
    573         }
    574 
    575         return halCmd(NANOHUB_HAL_EXT_APP_DELETE, argv[2]);
    576     } else if (strcmp(argv[1], "download") == 0) {
    577         int installCnt, uninstallCnt;
    578 
    579         if (argc != 2) {
    580             printf("Wrong arg number\n");
    581             return 1;
    582         }
    583         downloadNanohub();
    584         for (i = 0; i < MAX_DOWNLOAD_RETRIES; i++) {
    585             int updateCnt = parseConfigAppInfo(&installCnt, &uninstallCnt);
    586             if (updateCnt > 0) {
    587                 if (i == MAX_DOWNLOAD_RETRIES - 1) {
    588                     LOGE("Download failed after %d retries; erasing all apps "
    589                          "before final attempt", i);
    590                     eraseSharedArea();
    591                     parseConfigAppInfo(&installCnt, &uninstallCnt);
    592                 }
    593                 removeApps(uninstallCnt);
    594                 downloadApps(installCnt);
    595                 resetHub();
    596             } else if (!updateCnt){
    597                 return 0;
    598             }
    599         }
    600 
    601         if (parseConfigAppInfo(&installCnt, &uninstallCnt) != 0) {
    602             LOGE("Failed to download all apps!");
    603             return 1;
    604         }
    605         return 0;
    606     } else {
    607         printf("Unsupported action: %s\n", argv[1]);
    608         return 1;
    609     }
    610 
    611     while (!fileWriteData("/dev/nanohub", pConfigCmd, length))
    612         continue;
    613 
    614     if (pConfigCmd != &mConfigCmd)
    615         free(pConfigCmd);
    616 
    617     if (drain) {
    618         signal(SIGINT, sig_handle);
    619         fd = open("/dev/nanohub", O_RDONLY);
    620         while (!stop) {
    621             (void) read(fd, buf, buf_size);
    622         }
    623         close(fd);
    624     }
    625     return 0;
    626 }
    627