/* * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #define LOG_TAG "nanoapp_cmd" #include <assert.h> #include <errno.h> #include <fcntl.h> #include <inttypes.h> #include <signal.h> #include <stdbool.h> #include <stdint.h> #include <stdio.h> #include <stdlib.h> #include <sys/stat.h> #include <sys/types.h> #include <string.h> #include <unistd.h> #include <android/log.h> #include <nanohub/nanohub.h> #include <eventnums.h> #include <sensType.h> #define SENSOR_RATE_ONCHANGE 0xFFFFFF01UL #define SENSOR_RATE_ONESHOT 0xFFFFFF02UL #define SENSOR_HZ(_hz) ((uint32_t)((_hz) * 1024.0f)) #define MAX_APP_NAME_LEN 32 #define MAX_INSTALL_CNT 8 #define MAX_UNINSTALL_CNT 8 #define MAX_DOWNLOAD_RETRIES 4 #define UNINSTALL_CMD "uninstall" #define NANOHUB_HAL_EXT_APPS_ON 0 #define NANOHUB_HAL_EXT_APPS_OFF 1 #define NANOHUB_HAL_EXT_APP_DELETE 2 #define LOGE(fmt, ...) do { \ __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, fmt, ##__VA_ARGS__); \ printf(fmt "\n", ##__VA_ARGS__); \ } while (0) enum ConfigCmds { CONFIG_CMD_DISABLE = 0, CONFIG_CMD_ENABLE = 1, CONFIG_CMD_FLUSH = 2, CONFIG_CMD_CFG_DATA = 3, CONFIG_CMD_CALIBRATE = 4, }; struct ConfigCmd { uint32_t evtType; uint64_t latency; uint32_t rate; uint8_t sensorType; uint8_t cmd; uint16_t flags; uint8_t data[]; } __attribute__((packed)); struct HalCmd { struct HostMsgHdr hdr; uint8_t cmd; uint64_t appId; } __attribute__((packed)); struct App { uint32_t num; uint64_t id; uint32_t version; uint32_t size; }; struct LedsCfg { uint32_t led_num; uint32_t value; } __attribute__((packed)); static int setType(struct ConfigCmd *cmd, char *sensor) { if (strcmp(sensor, "accel") == 0) { cmd->sensorType = SENS_TYPE_ACCEL; } else if (strcmp(sensor, "gyro") == 0) { cmd->sensorType = SENS_TYPE_GYRO; } else if (strcmp(sensor, "mag") == 0) { cmd->sensorType = SENS_TYPE_MAG; } else if (strcmp(sensor, "uncal_accel") == 0) { cmd->sensorType = SENS_TYPE_ACCEL; } else if (strcmp(sensor, "uncal_gyro") == 0) { cmd->sensorType = SENS_TYPE_GYRO; } else if (strcmp(sensor, "uncal_mag") == 0) { cmd->sensorType = SENS_TYPE_MAG; } else if (strcmp(sensor, "als") == 0) { cmd->sensorType = SENS_TYPE_ALS; } else if (strcmp(sensor, "prox") == 0) { cmd->sensorType = SENS_TYPE_PROX; } else if (strcmp(sensor, "baro") == 0) { cmd->sensorType = SENS_TYPE_BARO; } else if (strcmp(sensor, "temp") == 0) { cmd->sensorType = SENS_TYPE_TEMP; } else if (strcmp(sensor, "ambient_temp") == 0) { cmd->sensorType = SENS_TYPE_AMBIENT_TEMP; } else if (strcmp(sensor, "orien") == 0) { cmd->sensorType = SENS_TYPE_ORIENTATION; } else if (strcmp(sensor, "gravity") == 0) { cmd->sensorType = SENS_TYPE_GRAVITY; } else if (strcmp(sensor, "geomag") == 0) { cmd->sensorType = SENS_TYPE_GEO_MAG_ROT_VEC; } else if (strcmp(sensor, "linear_acc") == 0) { cmd->sensorType = SENS_TYPE_LINEAR_ACCEL; } else if (strcmp(sensor, "rotation") == 0) { cmd->sensorType = SENS_TYPE_ROTATION_VECTOR; } else if (strcmp(sensor, "game") == 0) { cmd->sensorType = SENS_TYPE_GAME_ROT_VECTOR; } else if (strcmp(sensor, "win_orien") == 0) { cmd->sensorType = SENS_TYPE_WIN_ORIENTATION; cmd->rate = SENSOR_RATE_ONCHANGE; } else if (strcmp(sensor, "tilt") == 0) { cmd->sensorType = SENS_TYPE_TILT; cmd->rate = SENSOR_RATE_ONCHANGE; } else if (strcmp(sensor, "step_det") == 0) { cmd->sensorType = SENS_TYPE_STEP_DETECT; cmd->rate = SENSOR_RATE_ONCHANGE; } else if (strcmp(sensor, "step_cnt") == 0) { cmd->sensorType = SENS_TYPE_STEP_COUNT; cmd->rate = SENSOR_RATE_ONCHANGE; } else if (strcmp(sensor, "double_tap") == 0) { cmd->sensorType = SENS_TYPE_DOUBLE_TAP; cmd->rate = SENSOR_RATE_ONCHANGE; } else if (strcmp(sensor, "flat") == 0) { cmd->sensorType = SENS_TYPE_FLAT; cmd->rate = SENSOR_RATE_ONCHANGE; } else if (strcmp(sensor, "anymo") == 0) { cmd->sensorType = SENS_TYPE_ANY_MOTION; cmd->rate = SENSOR_RATE_ONCHANGE; } else if (strcmp(sensor, "nomo") == 0) { cmd->sensorType = SENS_TYPE_NO_MOTION; cmd->rate = SENSOR_RATE_ONCHANGE; } else if (strcmp(sensor, "sigmo") == 0) { cmd->sensorType = SENS_TYPE_SIG_MOTION; cmd->rate = SENSOR_RATE_ONESHOT; } else if (strcmp(sensor, "gesture") == 0) { cmd->sensorType = SENS_TYPE_GESTURE; cmd->rate = SENSOR_RATE_ONESHOT; } else if (strcmp(sensor, "hall") == 0) { cmd->sensorType = SENS_TYPE_HALL; cmd->rate = SENSOR_RATE_ONCHANGE; } else if (strcmp(sensor, "vsync") == 0) { cmd->sensorType = SENS_TYPE_VSYNC; cmd->rate = SENSOR_RATE_ONCHANGE; } else if (strcmp(sensor, "activity") == 0) { cmd->sensorType = SENS_TYPE_ACTIVITY; cmd->rate = SENSOR_RATE_ONCHANGE; } else if (strcmp(sensor, "twist") == 0) { cmd->sensorType = SENS_TYPE_DOUBLE_TWIST; cmd->rate = SENSOR_RATE_ONCHANGE; } else if (strcmp(sensor, "leds") == 0) { cmd->sensorType = SENS_TYPE_LEDS; } else if (strcmp(sensor, "leds_i2c") == 0) { cmd->sensorType = SENS_TYPE_LEDS_I2C; } else if (strcmp(sensor, "humidity") == 0) { cmd->sensorType = SENS_TYPE_HUMIDITY; } else { return 1; } return 0; } bool drain = false; bool stop = false; char *buf; int nread, buf_size = 2048; struct App apps[32]; uint8_t appCount; char appsToInstall[MAX_INSTALL_CNT][MAX_APP_NAME_LEN+1]; uint64_t appsToUninstall[MAX_UNINSTALL_CNT]; void sig_handle(__attribute__((unused)) int sig) { assert(sig == SIGINT); printf("Terminating...\n"); stop = true; } FILE *openFile(const char *fname, const char *mode) { FILE *f = fopen(fname, mode); if (f == NULL) { LOGE("Failed to open %s: err=%d [%s]", fname, errno, strerror(errno)); } return f; } void parseInstalledAppInfo() { FILE *fp; char *line = NULL; size_t len; ssize_t numRead; appCount = 0; fp = openFile("/sys/class/nanohub/nanohub/app_info", "r"); if (!fp) return; while ((numRead = getline(&line, &len, fp)) != -1) { struct App *currApp = &apps[appCount++]; sscanf(line, "app: %d id: %" PRIx64 " ver: %" PRIx32 " size: %" PRIx32 "\n", &currApp->num, &currApp->id, &currApp->version, &currApp->size); } fclose(fp); if (line) free(line); } struct App *findApp(uint64_t appId) { uint8_t i; for (i = 0; i < appCount; i++) { if (apps[i].id == appId) { return &apps[i]; } } return NULL; } int findAppIdByName(char *name, uint64_t *appId) { FILE *fp; char *line = NULL; size_t len; ssize_t numRead; int ret = 0; fp = openFile("/vendor/firmware/napp_list.cfg", "r"); if (!fp) return -1; while ((numRead = getline(&line, &len, fp)) != -1) { char entry[MAX_APP_NAME_LEN+1]; uint32_t appVersion; sscanf(line, "%" STRINGIFY(MAX_APP_NAME_LEN) "s %" PRIx64 " %" PRIx32 "\n", entry, appId, &appVersion); if (strncmp(entry, name, MAX_APP_NAME_LEN) == 0) { ret = 1; break; } } fclose(fp); if (line) free(line); return ret; } int parseConfigAppInfo(int *installCnt, int *uninstallCnt) { FILE *fp; char *line = NULL; size_t len; ssize_t numRead; fp = openFile("/vendor/firmware/napp_list.cfg", "r"); if (!fp) return -1; parseInstalledAppInfo(); *installCnt = *uninstallCnt = 0; while (((numRead = getline(&line, &len, fp)) != -1) && (*installCnt < MAX_INSTALL_CNT) && (*uninstallCnt < MAX_UNINSTALL_CNT)) { uint64_t appId; uint32_t appVersion; struct App *installedApp; sscanf(line, "%" STRINGIFY(MAX_APP_NAME_LEN) "s %" PRIx64 " %" PRIx32 "\n", appsToInstall[*installCnt], &appId, &appVersion); installedApp = findApp(appId); if (strncmp(appsToInstall[*installCnt], UNINSTALL_CMD, MAX_APP_NAME_LEN) == 0) { if (installedApp) { appsToUninstall[*uninstallCnt] = appId; (*uninstallCnt)++; } } else if (!installedApp || (installedApp->version < appVersion)) { (*installCnt)++; } } fclose(fp); if (line) free(line); return *installCnt + *uninstallCnt; } bool fileWriteData(const char *fname, const void *data, size_t size) { int fd; bool result; fd = open(fname, O_WRONLY); if (fd < 0) { LOGE("Failed to open %s: err=%d [%s]", fname, errno, strerror(errno)); return false; } result = true; if ((size_t)write(fd, data, size) != size) { LOGE("Failed to write to %s; err=%d [%s]", fname, errno, strerror(errno)); result = false; } close(fd); return result; } void downloadNanohub() { char c = '1'; printf("Updating nanohub OS [if required]..."); fflush(stdout); if (fileWriteData("/sys/class/nanohub/nanohub/download_bl", &c, sizeof(c))) printf("done\n"); } bool sendCmd(uint8_t cmd, uint64_t appId) { struct HalCmd msg; msg.hdr.eventId = EVT_APP_FROM_HOST; msg.hdr.appId = APP_ID_MAKE(NANOHUB_VENDOR_GOOGLE, 0); msg.hdr.len = sizeof(msg) - sizeof(msg.hdr); // payload length msg.cmd = cmd; memcpy(&msg.appId, &appId, sizeof(uint64_t)); return fileWriteData("/dev/nanohub", &msg, sizeof(msg)); } int halCmd(uint8_t cmd, char *arg) { uint64_t appId; char *endptr = arg + strlen(arg); if (strcmp(arg, UNINSTALL_CMD) == 0) { printf("%s is not a valid app name\n", arg); return 1; } if ((findAppIdByName(arg, &appId) == 1) || (appId = strtoull(arg, &endptr, 16)) > 0) { if (*endptr != '\0') { printf("Couldn't find nanoapp '%s' in napp_list.cfg\n", arg); return 1; } else if (cmd == NANOHUB_HAL_EXT_APPS_ON) printf("Loading "); else if (cmd == NANOHUB_HAL_EXT_APPS_OFF) printf("Unloading "); else if (cmd == NANOHUB_HAL_EXT_APP_DELETE) printf("Deleting "); else { printf("Unrecognized cmd: %d\n", cmd); return 1; } printf("\"0x%016" PRIx64 "\"...", appId); fflush(stdout); if (sendCmd(cmd, appId)) printf("done\n"); return 0; } else { printf("Couldn't find nanoapp '%s' in napp_list.cfg\n", arg); return 1; } } void removeApps(int updateCnt) { int i; for (i = 0; i < updateCnt; i++) { printf("Deleting \"0x%016" PRIx64 "\"...", appsToUninstall[i]); fflush(stdout); if (sendCmd(NANOHUB_HAL_EXT_APP_DELETE, appsToUninstall[i])) printf("done\n"); } } void downloadApps(int updateCnt) { int i; for (i = 0; i < updateCnt; i++) { printf("Downloading \"%s.napp\"...", appsToInstall[i]); fflush(stdout); if (fileWriteData("/sys/class/nanohub/nanohub/download_app", appsToInstall[i], strlen(appsToInstall[i]))) printf("done\n"); } } void eraseSharedArea() { char c = '1'; printf("Erasing entire nanohub shared area..."); fflush(stdout); if (fileWriteData("/sys/class/nanohub/nanohub/erase_shared", &c, sizeof(c))) printf("done\n"); } void resetHub() { char c = '1'; printf("Resetting nanohub..."); fflush(stdout); if (fileWriteData("/sys/class/nanohub/nanohub/reset", &c, sizeof(c))) printf("done\n"); } int main(int argc, char *argv[]) { struct ConfigCmd mConfigCmd; struct ConfigCmd *pConfigCmd = &mConfigCmd; size_t length = sizeof(mConfigCmd); int fd; int i; if (argc < 3 && (argc < 2 || strcmp(argv[1], "download") != 0)) { printf("usage: %s <action> <sensor> <data> -d\n", argv[0]); printf(" action: config|cfgdata|calibrate|flush\n"); printf(" sensor: (uncal_)accel|(uncal_)gyro|(uncal_)mag|als|prox|baro|temp|orien\n"); printf(" gravity|geomag|linear_acc|rotation|game\n"); printf(" win_orien|tilt|step_det|step_cnt|double_tap\n"); printf(" flat|anymo|nomo|sigmo|gesture|hall|vsync\n"); printf(" activity|twist|leds|leds_i2c|humidity|ambient_temp\n"); printf(" data: config: <true|false> <rate in Hz> <latency in u-sec>\n"); printf(" cfgdata: leds: led_num value\n"); printf(" calibrate: [N.A.]\n"); printf(" flush: [N.A.]\n"); printf(" -d: if specified, %s will keep draining /dev/nanohub until cancelled.\n", argv[0]); printf("usage: %s <cmd> [app]\n", argv[0]); printf(" cmd: download|load|unload|delete\n"); printf(" app: appId or name from napp_list.cfg\n"); return 1; } if (strcmp(argv[1], "config") == 0) { if (argc != 6 && argc != 7) { printf("Wrong arg number\n"); return 1; } if (argc == 7) { if(strcmp(argv[6], "-d") == 0) { drain = true; } else { printf("Last arg unsupported, ignored.\n"); } } if (strcmp(argv[3], "true") == 0) mConfigCmd.cmd = CONFIG_CMD_ENABLE; else if (strcmp(argv[3], "false") == 0) { mConfigCmd.cmd = CONFIG_CMD_DISABLE; } else { printf("Unsupported data: %s For action: %s\n", argv[3], argv[1]); return 1; } mConfigCmd.evtType = EVT_NO_SENSOR_CONFIG_EVENT; mConfigCmd.rate = SENSOR_HZ((float)atoi(argv[4])); mConfigCmd.latency = atoi(argv[5]) * 1000ull; if (setType(&mConfigCmd, argv[2])) { printf("Unsupported sensor: %s For action: %s\n", argv[2], argv[1]); return 1; } } else if (strcmp(argv[1], "cfgdata") == 0) { mConfigCmd.evtType = EVT_NO_SENSOR_CONFIG_EVENT; mConfigCmd.rate = 0; mConfigCmd.latency = 0; mConfigCmd.cmd = CONFIG_CMD_CFG_DATA; if (setType(&mConfigCmd, argv[2])) { printf("Unsupported sensor: %s For action: %s\n", argv[2], argv[1]); return 1; } if (mConfigCmd.sensorType == SENS_TYPE_LEDS || mConfigCmd.sensorType == SENS_TYPE_LEDS_I2C) { struct LedsCfg mLedsCfg; if (argc != 5) { printf("Wrong arg number\n"); return 1; } length = sizeof(struct ConfigCmd) + sizeof(struct LedsCfg *); pConfigCmd = (struct ConfigCmd *)malloc(length); if (!pConfigCmd) return 1; mLedsCfg.led_num = atoi(argv[3]); mLedsCfg.value = atoi(argv[4]); memcpy(pConfigCmd, &mConfigCmd, sizeof(mConfigCmd)); memcpy(pConfigCmd->data, &mLedsCfg, sizeof(mLedsCfg)); } else { printf("Unsupported sensor: %s For action: %s\n", argv[2], argv[1]); return 1; } } else if (strcmp(argv[1], "calibrate") == 0) { if (argc != 3) { printf("Wrong arg number\n"); return 1; } mConfigCmd.evtType = EVT_NO_SENSOR_CONFIG_EVENT; mConfigCmd.rate = 0; mConfigCmd.latency = 0; mConfigCmd.cmd = CONFIG_CMD_CALIBRATE; if (setType(&mConfigCmd, argv[2])) { printf("Unsupported sensor: %s For action: %s\n", argv[2], argv[1]); return 1; } } else if (strcmp(argv[1], "flush") == 0) { if (argc != 3) { printf("Wrong arg number\n"); return 1; } mConfigCmd.evtType = EVT_NO_SENSOR_CONFIG_EVENT; mConfigCmd.rate = 0; mConfigCmd.latency = 0; mConfigCmd.cmd = CONFIG_CMD_FLUSH; if (setType(&mConfigCmd, argv[2])) { printf("Unsupported sensor: %s For action: %s\n", argv[2], argv[1]); return 1; } } else if (strcmp(argv[1], "load") == 0) { if (argc != 3) { printf("Wrong arg number\n"); return 1; } return halCmd(NANOHUB_HAL_EXT_APPS_ON, argv[2]); } else if (strcmp(argv[1], "unload") == 0) { if (argc != 3) { printf("Wrong arg number\n"); return 1; } return halCmd(NANOHUB_HAL_EXT_APPS_OFF, argv[2]); } else if (strcmp(argv[1], "delete") == 0) { if (argc != 3) { printf("Wrong arg number\n"); return 1; } return halCmd(NANOHUB_HAL_EXT_APP_DELETE, argv[2]); } else if (strcmp(argv[1], "download") == 0) { int installCnt, uninstallCnt; if (argc != 2) { printf("Wrong arg number\n"); return 1; } downloadNanohub(); for (i = 0; i < MAX_DOWNLOAD_RETRIES; i++) { int updateCnt = parseConfigAppInfo(&installCnt, &uninstallCnt); if (updateCnt > 0) { if (i == MAX_DOWNLOAD_RETRIES - 1) { LOGE("Download failed after %d retries; erasing all apps " "before final attempt", i); eraseSharedArea(); parseConfigAppInfo(&installCnt, &uninstallCnt); } removeApps(uninstallCnt); downloadApps(installCnt); resetHub(); } else if (!updateCnt){ return 0; } } if (parseConfigAppInfo(&installCnt, &uninstallCnt) != 0) { LOGE("Failed to download all apps!"); return 1; } return 0; } else { printf("Unsupported action: %s\n", argv[1]); return 1; } while (!fileWriteData("/dev/nanohub", pConfigCmd, length)) continue; if (pConfigCmd != &mConfigCmd) free(pConfigCmd); if (drain) { signal(SIGINT, sig_handle); fd = open("/dev/nanohub", O_RDONLY); while (!stop) { (void) read(fd, buf, buf_size); } close(fd); } return 0; }