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