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