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
StrToCommand(const char * command_name)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
PrintUsage(const char * name)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 */
ValidateArgs(std::unique_ptr<ParsedArgs> & args,const char * name)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
ParseRate(const std::string & param,SensorSpec & spec)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.
ParseSensorArg(std::vector<SensorSpec> & sensors,const char * arg_str,const char * name)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
ParseArgs(int argc,char ** argv)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
GetContextHub(std::unique_ptr<ParsedArgs> & args)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__
SignalHandler(int sig)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
TerminateHandler()398 static void TerminateHandler() {
399 AndroidContextHub::TerminateHandler();
400 std::abort();
401 }
402
SetHandlers()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
main(int argc,char ** argv)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