Home | History | Annotate | Download | only in nanotool
      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 #include <getopt.h>
     18 #include <signal.h>
     19 #include <unistd.h>
     20 
     21 #include <cstdlib>
     22 #include <cstring>
     23 #include <memory>
     24 #include <sstream>
     25 #include <tuple>
     26 #include <vector>
     27 
     28 #include "contexthub.h"
     29 #include "log.h"
     30 
     31 #ifdef __ANDROID__
     32 #include "androidcontexthub.h"
     33 #else
     34 #include "cp2130.h"
     35 #include "usbcontext.h"
     36 #include "usbcontexthub.h"
     37 #endif
     38 
     39 using namespace android;
     40 
     41 enum class NanotoolCommand {
     42     Invalid,
     43     Disable,
     44     DisableAll,
     45     Calibrate,
     46     Test,
     47     Read,
     48     Poll,
     49     LoadCalibration,
     50     Flash,
     51     GetBridgeVer,
     52 };
     53 
     54 struct ParsedArgs {
     55     NanotoolCommand command = NanotoolCommand::Poll;
     56     std::vector<SensorSpec> sensors;
     57     int count = 0;
     58     bool logging_enabled = false;
     59     std::string filename;
     60     int device_index = 0;
     61 };
     62 
     63 static NanotoolCommand StrToCommand(const char *command_name) {
     64     static const std::vector<std::tuple<std::string, NanotoolCommand>> cmds = {
     65         std::make_tuple("disable",     NanotoolCommand::Disable),
     66         std::make_tuple("disable_all", NanotoolCommand::DisableAll),
     67         std::make_tuple("calibrate",   NanotoolCommand::Calibrate),
     68         std::make_tuple("cal",         NanotoolCommand::Calibrate),
     69         std::make_tuple("test",        NanotoolCommand::Test),
     70         std::make_tuple("read",        NanotoolCommand::Read),
     71         std::make_tuple("poll",        NanotoolCommand::Poll),
     72         std::make_tuple("load_cal",    NanotoolCommand::LoadCalibration),
     73         std::make_tuple("flash",       NanotoolCommand::Flash),
     74         std::make_tuple("bridge_ver",  NanotoolCommand::GetBridgeVer),
     75     };
     76 
     77     if (!command_name) {
     78         return NanotoolCommand::Invalid;
     79     }
     80 
     81     for (size_t i = 0; i < cmds.size(); i++) {
     82         std::string name;
     83         NanotoolCommand cmd;
     84 
     85         std::tie(name, cmd) = cmds[i];
     86         if (name.compare(command_name) == 0) {
     87             return cmd;
     88         }
     89     }
     90 
     91     return NanotoolCommand::Invalid;
     92 }
     93 
     94 static void PrintUsage(const char *name) {
     95     const char *help_text =
     96         "options:\n"
     97         "  -x, --cmd          Argument must be one of:\n"
     98         "                        bridge_ver: retrieve bridge version information (not\n"
     99         "                           supported on all devices)\n"
    100         "                        disable: send a disable request for one sensor\n"
    101         "                        disable_all: send a disable request for all sensors\n"
    102         "                        calibrate: disable the sensor, then perform the sensor\n"
    103         "                           calibration routine\n"
    104         "                        test: run a sensor's self-test routine\n"
    105 #ifndef __ANDROID__
    106         "                        flash: load a new firmware image to the hub\n"
    107 #endif
    108         "                        load_cal: send data from calibration file to hub\n"
    109         "                        poll (default): enable the sensor, output received\n"
    110         "                           events, then disable the sensor before exiting\n"
    111         "                        read: output events for the given sensor, or all events\n"
    112         "                           if no sensor specified\n"
    113         "\n"
    114         "  -s, --sensor       Specify sensor type, and parameters for the command.\n"
    115         "                     Format is sensor_type[:rate[:latency_ms]][=cal_ref].\n"
    116         "                     See below for a complete list sensor types. A rate is\n"
    117         "                     required when enabling a sensor, but latency is optional\n"
    118         "                     and defaults to 0. Rate can be specified in Hz, or as one\n"
    119         "                     of the special values \"onchange\", \"ondemand\", or\n"
    120         "                     \"oneshot\".\n"
    121         "                     Some sensors require a ground truth value for calibration.\n"
    122         "                     Use the cal_ref parameter for this purpose (it's parsed as\n"
    123         "                     a float).\n"
    124         "                     This argument can be repeated to perform a command on\n"
    125         "                     multiple sensors.\n"
    126         "\n"
    127         "  -c, --count        Number of samples to read before exiting, or set to 0 to\n"
    128         "                     read indefinitely (the default behavior)\n"
    129         "\n"
    130         "  -f, --file\n"
    131         "                     Specifies the file to be used with flash.\n"
    132         "\n"
    133         "  -l, --log          Outputs logs from the sensor hub as they become available.\n"
    134         "                     The logs will be printed inline with sensor samples.\n"
    135         "                     The default is for log messages to be ignored.\n"
    136 #ifndef __ANDROID__
    137         // This option is only applicable when connecting over USB
    138         "\n"
    139         "  -i, --index        Selects the device to work with by specifying the index\n"
    140         "                     into the device list (default: 0)\n"
    141 #endif
    142         "\n"
    143         "  -v, -vv            Output verbose/extra verbose debugging information\n";
    144 
    145     fprintf(stderr, "%s %s\n\n", name, NANOTOOL_VERSION_STR);
    146     fprintf(stderr, "Usage: %s [options]\n\n%s\n", name, help_text);
    147     fprintf(stderr, "Supported sensors: %s\n\n",
    148             ContextHub::ListAllSensorAbbrevNames().c_str());
    149     fprintf(stderr, "Examples:\n"
    150                     "  %s -s accel:50\n"
    151                     "  %s -s accel:50:1000 -s gyro:50:1000\n"
    152                     "  %s -s prox:onchange\n"
    153                     "  %s -x calibrate -s baro=1000\n",
    154             name, name, name, name);
    155 }
    156 
    157 /*
    158  * Performs higher-level argument validation beyond just parsing the parameters,
    159  * for example check whether a required argument is present when the command is
    160  * set to a specific value.
    161  */
    162 static bool ValidateArgs(std::unique_ptr<ParsedArgs>& args, const char *name) {
    163     if (!args->sensors.size()
    164           && (args->command == NanotoolCommand::Disable
    165                 || args->command == NanotoolCommand::Calibrate
    166                 || args->command == NanotoolCommand::Test
    167                 || args->command == NanotoolCommand::Poll)) {
    168         fprintf(stderr, "%s: At least 1 sensor must be specified for this "
    169                         "command (use -s)\n",
    170                 name);
    171         return false;
    172     }
    173 
    174     if (args->command == NanotoolCommand::Flash
    175             && args->filename.empty()) {
    176         fprintf(stderr, "%s: A filename must be specified for this command "
    177                         "(use -f)\n",
    178                 name);
    179         return false;
    180     }
    181 
    182     if (args->command == NanotoolCommand::Poll) {
    183         for (unsigned int i = 0; i < args->sensors.size(); i++) {
    184             if (args->sensors[i].special_rate == SensorSpecialRate::None
    185                   && args->sensors[i].rate_hz < 0) {
    186                 fprintf(stderr, "%s: Sample rate must be specified for sensor "
    187                         "%s\n", name,
    188                         ContextHub::SensorTypeToAbbrevName(
    189                             args->sensors[i].sensor_type).c_str());
    190                 return false;
    191             }
    192         }
    193     }
    194 
    195     if (args->command == NanotoolCommand::Calibrate) {
    196         for (unsigned int i = 0; i < args->sensors.size(); i++) {
    197             if (!args->sensors[i].have_cal_ref
    198                   && (args->sensors[i].sensor_type == SensorType::Barometer
    199                         || args->sensors[i].sensor_type ==
    200                              SensorType::AmbientLightSensor)) {
    201                 fprintf(stderr, "%s: Calibration reference required for sensor "
    202                                 "%s (for example: -s baro=1000)\n", name,
    203                         ContextHub::SensorTypeToAbbrevName(
    204                             args->sensors[i].sensor_type).c_str());
    205                 return false;
    206             }
    207         }
    208     }
    209 
    210     return true;
    211 }
    212 
    213 static bool ParseRate(const std::string& param, SensorSpec& spec) {
    214     static const std::vector<std::tuple<std::string, SensorSpecialRate>> rates = {
    215         std::make_tuple("ondemand", SensorSpecialRate::OnDemand),
    216         std::make_tuple("onchange", SensorSpecialRate::OnChange),
    217         std::make_tuple("oneshot",  SensorSpecialRate::OneShot),
    218     };
    219 
    220     for (size_t i = 0; i < rates.size(); i++) {
    221         std::string name;
    222         SensorSpecialRate rate;
    223 
    224         std::tie(name, rate) = rates[i];
    225         if (param == name) {
    226             spec.special_rate = rate;
    227             return true;
    228         }
    229     }
    230 
    231     spec.rate_hz = std::stof(param);
    232     if (spec.rate_hz < 0) {
    233         return false;
    234     }
    235 
    236     return true;
    237 }
    238 
    239 // Parse a sensor argument in the form of "sensor_name[:rate[:latency]][=cal_ref]"
    240 // into a SensorSpec, and add it to ParsedArgs.
    241 static bool ParseSensorArg(std::vector<SensorSpec>& sensors, const char *arg_str,
    242         const char *name) {
    243     SensorSpec spec;
    244     std::string param;
    245     std::string pre_cal_ref;
    246     std::stringstream full_arg_ss(arg_str);
    247     unsigned int index = 0;
    248 
    249     while (std::getline(full_arg_ss, param, '=')) {
    250         if (index == 0) {
    251             pre_cal_ref = param;
    252         } else if (index == 1) {
    253             spec.cal_ref = std::stof(param);
    254             spec.have_cal_ref = true;
    255         } else {
    256             fprintf(stderr, "%s: Only one calibration reference may be "
    257                             "supplied\n", name);
    258             return false;
    259         }
    260         index++;
    261     }
    262 
    263     index = 0;
    264     std::stringstream pre_cal_ref_ss(pre_cal_ref);
    265     while (std::getline(pre_cal_ref_ss, param, ':')) {
    266         if (index == 0) { // Parse sensor type
    267             spec.sensor_type = ContextHub::SensorAbbrevNameToType(param);
    268             if (spec.sensor_type == SensorType::Invalid_) {
    269                 fprintf(stderr, "%s: Invalid sensor name '%s'\n",
    270                         name, param.c_str());
    271                 return false;
    272             }
    273         } else if (index == 1) { // Parse sample rate
    274             if (!ParseRate(param, spec)) {
    275                 fprintf(stderr, "%s: Invalid sample rate %s\n", name,
    276                         param.c_str());
    277                 return false;
    278             }
    279         } else if (index == 2) { // Parse latency
    280             long long latency_ms = std::stoll(param);
    281             if (latency_ms < 0) {
    282                 fprintf(stderr, "%s: Invalid latency %lld\n", name, latency_ms);
    283                 return false;
    284             }
    285             spec.latency_ns = static_cast<uint64_t>(latency_ms) * 1000000;
    286         } else {
    287             fprintf(stderr, "%s: Too many arguments in -s", name);
    288             return false;
    289         }
    290         index++;
    291     }
    292 
    293     sensors.push_back(spec);
    294     return true;
    295 }
    296 
    297 static std::unique_ptr<ParsedArgs> ParseArgs(int argc, char **argv) {
    298     static const struct option long_opts[] = {
    299         {"cmd",     required_argument, nullptr, 'x'},
    300         {"sensor",  required_argument, nullptr, 's'},
    301         {"count",   required_argument, nullptr, 'c'},
    302         {"flash",   required_argument, nullptr, 'f'},
    303         {"log",     no_argument,       nullptr, 'l'},
    304         {"index",   required_argument, nullptr, 'i'},
    305         {}  // Indicates the end of the option list
    306     };
    307 
    308     auto args = std::unique_ptr<ParsedArgs>(new ParsedArgs());
    309     int index = 0;
    310     while (42) {
    311         int c = getopt_long(argc, argv, "x:s:c:f:v::li:", long_opts, &index);
    312         if (c == -1) {
    313             break;
    314         }
    315 
    316         switch (c) {
    317           case 'x': {
    318             args->command = StrToCommand(optarg);
    319             if (args->command == NanotoolCommand::Invalid) {
    320                 fprintf(stderr, "%s: Invalid command '%s'\n", argv[0], optarg);
    321                 return nullptr;
    322             }
    323             break;
    324           }
    325           case 's': {
    326             if (!ParseSensorArg(args->sensors, optarg, argv[0])) {
    327                 return nullptr;
    328             }
    329             break;
    330           }
    331           case 'c': {
    332             args->count = atoi(optarg);
    333             if (args->count < 0) {
    334                 fprintf(stderr, "%s: Invalid sample count %d\n",
    335                         argv[0], args->count);
    336                 return nullptr;
    337             }
    338             break;
    339           }
    340           case 'v': {
    341             if (optarg && optarg[0] == 'v') {
    342                 Log::SetLevel(Log::LogLevel::Debug);
    343             } else {
    344                 Log::SetLevel(Log::LogLevel::Info);
    345             }
    346             break;
    347           }
    348           case 'l': {
    349             args->logging_enabled = true;
    350             break;
    351           }
    352           case 'f': {
    353             if (optarg) {
    354                 args->filename = std::string(optarg);
    355             } else {
    356                 fprintf(stderr, "File requires a filename\n");
    357                 return nullptr;
    358             }
    359             break;
    360           }
    361           case 'i': {
    362             args->device_index = atoi(optarg);
    363             if (args->device_index < 0) {
    364                 fprintf(stderr, "%s: Invalid device index %d\n", argv[0],
    365                         args->device_index);
    366                 return nullptr;
    367             }
    368             break;
    369           }
    370           default:
    371             return nullptr;
    372         }
    373     }
    374 
    375     if (!ValidateArgs(args, argv[0])) {
    376         return nullptr;
    377     }
    378     return args;
    379 }
    380 
    381 static std::unique_ptr<ContextHub> GetContextHub(std::unique_ptr<ParsedArgs>& args) {
    382 #ifdef __ANDROID__
    383     (void) args;
    384     return std::unique_ptr<AndroidContextHub>(new AndroidContextHub());
    385 #else
    386     return std::unique_ptr<UsbContextHub>(new UsbContextHub(args->device_index));
    387 #endif
    388 }
    389 
    390 #ifdef __ANDROID__
    391 static void SignalHandler(int sig) {
    392     // Catches a signal and does nothing, to allow any pending syscalls to be
    393     // exited with SIGINT and normal cleanup to occur. If SIGINT is sent a
    394     // second time, the system will invoke the standard handler.
    395     (void) sig;
    396 }
    397 
    398 static void TerminateHandler() {
    399     AndroidContextHub::TerminateHandler();
    400     std::abort();
    401 }
    402 
    403 static void SetHandlers() {
    404     struct sigaction sa;
    405     memset(&sa, 0, sizeof(sa));
    406     sa.sa_handler = SignalHandler;
    407     sigaction(SIGINT, &sa, NULL);
    408 
    409     std::set_terminate(TerminateHandler);
    410 }
    411 #endif
    412 
    413 int main(int argc, char **argv) {
    414     Log::Initialize(new PrintfLogger(), Log::LogLevel::Warn);
    415 
    416     // If no arguments given, print usage without any error messages
    417     if (argc == 1) {
    418         PrintUsage(argv[0]);
    419         return 1;
    420     }
    421 
    422     std::unique_ptr<ParsedArgs> args = ParseArgs(argc, argv);
    423     if (!args) {
    424         PrintUsage(argv[0]);
    425         return 1;
    426     }
    427 
    428 #ifdef __ANDROID__
    429     SetHandlers();
    430 #endif
    431 
    432     std::unique_ptr<ContextHub> hub = GetContextHub(args);
    433     if (!hub || !hub->Initialize()) {
    434         LOGE("Error initializing ContextHub");
    435         return -1;
    436     }
    437 
    438     hub->SetLoggingEnabled(args->logging_enabled);
    439 
    440     bool success = true;
    441     switch (args->command) {
    442       case NanotoolCommand::Disable:
    443         success = hub->DisableSensors(args->sensors);
    444         break;
    445       case NanotoolCommand::DisableAll:
    446         success = hub->DisableAllSensors();
    447         break;
    448       case NanotoolCommand::Read: {
    449         if (!args->sensors.size()) {
    450             hub->PrintAllEvents(args->count);
    451         } else {
    452             hub->PrintSensorEvents(args->sensors, args->count);
    453         }
    454         break;
    455       }
    456       case NanotoolCommand::Poll: {
    457         success = hub->EnableSensors(args->sensors);
    458         if (success) {
    459             hub->PrintSensorEvents(args->sensors, args->count);
    460         }
    461         break;
    462       }
    463       case NanotoolCommand::Calibrate: {
    464         hub->DisableSensors(args->sensors);
    465         success = hub->CalibrateSensors(args->sensors);
    466         break;
    467       }
    468       case NanotoolCommand::Test: {
    469         hub->DisableSensors(args->sensors);
    470 
    471         /* Most of drivers complete enable/disable after SPI/I2C callback
    472            transaction return. Since enable/disable functions return immediatly
    473            before it, some time ensure entire process is completed. */
    474         usleep(100000);
    475 
    476         success = hub->TestSensors(args->sensors);
    477         break;
    478       }
    479       case NanotoolCommand::LoadCalibration: {
    480         success = hub->LoadCalibration();
    481         break;
    482       }
    483       case NanotoolCommand::Flash: {
    484         success = hub->Flash(args->filename);
    485         break;
    486       }
    487       case NanotoolCommand::GetBridgeVer: {
    488         success = hub->PrintBridgeVersion();
    489         break;
    490       }
    491       default:
    492         LOGE("Command not implemented");
    493         return 1;
    494     }
    495 
    496     if (!success) {
    497         LOGE("Command failed");
    498         return -1;
    499     } else if (args->command != NanotoolCommand::Read
    500                    && args->command != NanotoolCommand::Poll) {
    501         printf("Operation completed successfully\n");
    502     }
    503 
    504     return 0;
    505 }
    506