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