blob: 5b9fc07187a0b11ba027acd22725be6667e36fcf [file] [log] [blame]
/*
* act.c - Actuator manipulation tool
* Copyright (C) 2017 Jiri Pirko <jiri@resnulli.us>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <stdbool.h>
#include <limits.h>
#include <getopt.h>
#include <errno.h>
#include <actuator.h>
#include <sys/select.h>
#include <linux/actuator.h>
#define pr_err(args...) fprintf(stderr, ##args)
#define pr_out(args...) \
do { \
fprintf(stdout, ##args); \
} while (0)
#define BIT(nr) (1UL << (nr))
#define ACT_OPT_HANDLE BIT(0)
#define ACT_OPT_VALUE BIT(1)
struct act_opts {
uint32_t present; /* flags of present items */
struct actuator_handle handle;
uint32_t value;
};
struct act {
struct actuator *actuator;
int argc;
char **argv;
struct act_opts opts;
};
static int act_argc(struct act *act)
{
return act->argc;
}
static char *act_argv(struct act *act)
{
if (act_argc(act) == 0)
return NULL;
return *act->argv;
}
static void act_arg_inc(struct act *act)
{
if (act_argc(act) == 0)
return;
act->argc--;
act->argv++;
}
static char *act_argv_next(struct act *act)
{
char *ret;
if (act_argc(act) == 0)
return NULL;
ret = *act->argv;
act_arg_inc(act);
return ret;
}
static int strcmpx(const char *str1, const char *str2)
{
if (strlen(str1) > strlen(str2))
return -1;
return strncmp(str1, str2, strlen(str1));
}
static bool act_argv_match(struct act *act, const char *pattern)
{
if (act_argc(act) == 0)
return false;
return strcmpx(act_argv(act), pattern) == 0;
}
static bool act_no_arg(struct act *act)
{
return act_argc(act) == 0;
}
static unsigned int strslashcount(char *str)
{
unsigned int count = 0;
char *pos = str;
while ((pos = strchr(pos, '/'))) {
count++;
pos++;
}
return count;
}
static int strslashrsplit(char *str, char **before, char **after)
{
char *slash;
slash = strrchr(str, '/');
if (!slash)
return -EINVAL;
*slash = '\0';
*before = str;
*after = slash + 1;
return 0;
}
static int strtouint32_t(const char *str, uint32_t *p_val)
{
char *endptr;
unsigned long int val;
val = strtoul(str, &endptr, 10);
if (endptr == str || *endptr != '\0')
return -EINVAL;
if (val > UINT_MAX)
return -ERANGE;
*p_val = val;
return 0;
}
static int act_argv_handle(struct act *act, struct actuator_handle *handle)
{
char *str = act_argv_next(act);
unsigned int slash_count;
char *indexstr = indexstr;
char *reststr = reststr;
int err;
if (!str) {
pr_err("Port identification (\"bus_name/dev_name/index\") expected.\n");
return -EINVAL;
}
slash_count = strslashcount(str);
if (slash_count != 2) {
pr_err("Wrong port identification string format.\n");
pr_err("Expected \"bus_name/dev_name/index\".\n");
return -EINVAL;
}
strslashrsplit(str, &reststr, &indexstr);
err = strtouint32_t(indexstr, &handle->index);
if (err) {
pr_err("Index \"%s\" is not a number or not within range\n",
indexstr);
return err;
}
strslashrsplit(reststr, (char **) &handle->bus_name,
(char **) &handle->dev_name);
return 0;
}
static int act_argv_uint32_t(struct act *act, uint32_t *p_val)
{
char *str = act_argv_next(act);
int err;
if (!str) {
pr_err("Unsigned number argument expected\n");
return -EINVAL;
}
err = strtouint32_t(str, p_val);
if (err) {
pr_err("\"%s\" is not a number or not within range\n", str);
return err;
}
return 0;
}
static int act_argv_parse(struct act *act, uint32_t o_required)
{
struct act_opts *opts = &act->opts;
uint32_t o_found = 0;
int err;
if (o_required & ACT_OPT_HANDLE) {
err = act_argv_handle(act, &opts->handle);
if (err)
return err;
o_found |= ACT_OPT_HANDLE;
}
while (act_argc(act)) {
if (act_argv_match(act, "value") && (o_required & ACT_OPT_VALUE)) {
act_arg_inc(act);
err = act_argv_uint32_t(act, &opts->value);
if (err)
return err;
o_found |= ACT_OPT_VALUE;
} else {
pr_err("Unknown option \"%s\"\n", act_argv(act));
return -EINVAL;
}
}
opts->present = o_found;
if ((o_required & ACT_OPT_VALUE) && !(o_found & ACT_OPT_VALUE)) {
pr_err("Value option expected.\n");
return -EINVAL;
}
return 0;
}
static void act_cmd_dev_help(void)
{
pr_err("Usage: act dev show [ DEV ]\n");
pr_err(" act dev move DEV value VALUE\n");
pr_err(" act dev stop DEV\n");
}
static void pr_out_handle_begin(struct act *act, struct actuator_handle *handle)
{
pr_out("%s/%s/%u:", handle->bus_name, handle->dev_name, handle->index);
}
static void pr_out_handle_end(struct act *act)
{
pr_out("\n");
}
static const char *act_event_type_name(enum actuator_event_type event_type)
{
switch (event_type) {
case ACTUATOR_EVENT_TYPE_GET:
return "get";
case ACTUATOR_EVENT_TYPE_NEW:
return "new";
case ACTUATOR_EVENT_TYPE_DEL:
return "del";
default:
return "<unknown event_type>";
}
}
static void pr_out_event_type_begin(struct act *act,
enum actuator_event_type event_type)
{
pr_out("%s: ", act_event_type_name(event_type));
}
static void pr_out_event_type_end(struct act *act)
{
}
static void pr_out_str(struct act *act, const char *label, const char *str)
{
pr_out(" %s %s", label, str);
}
static void pr_out_u32(struct act *act, const char *label, uint32_t val)
{
pr_out(" %s %u", label, val);
}
static const char *act_type_name(uint8_t type)
{
switch (type) {
case ACTUATOR_TYPE_LINEAR:
return "linear";
default:
return "<unknown type>";
}
}
static const char *act_units_name(uint8_t units)
{
switch (units) {
case ACTUATOR_UNITS_UM:
return "um";
default:
return "<unknown units>";
}
}
static const char *act_move_state_name(uint8_t move_state)
{
switch (move_state) {
case ACTUATOR_MOVE_STATE_STOPPED:
return "stopped";
case ACTUATOR_MOVE_STATE_POSITIVE:
return "positive";
case ACTUATOR_MOVE_STATE_NEGATIVE:
return "negative";
default:
return "<unknown move_state>";
}
}
static void pr_out_actuator_info(struct act *act, struct actuator_info *info)
{
struct actuator_handle handle;
actuator_info_handle(info, &handle);
pr_out_handle_begin(act, &handle);
pr_out_str(act, "driver_name", actuator_info_driver_name(info));
pr_out_str(act, "type", act_type_name(actuator_info_type(info)));
pr_out_str(act, "units", act_units_name(actuator_info_units(info)));
if (actuator_info_has_value(info))
pr_out_u32(act, "value", actuator_info_value(info));
if (actuator_info_has_move_state(info))
pr_out_str(act, "move_state",
act_move_state_name(actuator_info_move_state(info)));
pr_out_handle_end(act);
}
static void act_cmd_dev_show_cb(struct actuator *actuator,
struct actuator_info *info, void *cb_priv)
{
struct act *act = cb_priv;
pr_out_actuator_info(act, info);
}
static int act_cmd_dev_show(struct act *act)
{
return actuator_get(act->actuator, NULL, &act_cmd_dev_show_cb, act);
}
static int act_cmd_dev_move(struct act *act)
{
int err;
err = act_argv_parse(act, ACT_OPT_HANDLE | ACT_OPT_VALUE);
if (err)
return err;
return actuator_move(act->actuator, &act->opts.handle,
"v", act->opts.value);
}
static int act_cmd_dev_stop(struct act *act)
{
int err;
err = act_argv_parse(act, ACT_OPT_HANDLE);
if (err)
return err;
return actuator_stop(act->actuator, &act->opts.handle, "");
}
static void act_cmd_mon_show_cb(struct actuator *actuator,
struct actuator_info *info, void *cb_priv)
{
struct act *act = cb_priv;
pr_out_event_type_begin(act, actuator_info_event_type(info));
pr_out_actuator_info(act, info);
pr_out_event_type_end(act);
}
static int act_cmd_mon(struct act *act)
{
fd_set fds;
int fdmax;
struct timeval tv = {};
int fd = actuator_event_fd_get(act->actuator);
int ret;
int err;
FD_ZERO(&fds);
FD_SET(fd, &fds);
fdmax = fd + 1;
actuator_event_cb_set(act->actuator, ACTUATOR_EVENT_TYPE_GET,
&act_cmd_mon_show_cb, act);
actuator_event_cb_set(act->actuator, ACTUATOR_EVENT_TYPE_NEW,
&act_cmd_mon_show_cb, act);
actuator_event_cb_set(act->actuator, ACTUATOR_EVENT_TYPE_DEL,
&act_cmd_mon_show_cb, act);
do {
ret = select(fdmax, &fds, NULL, NULL, &tv);
if (ret == -1) {
if (errno == EINTR)
return 0;
return -errno;
}
err = actuator_event_process(act->actuator);
if (err)
return err;
} while (true);
}
static int act_cmd_dev(struct act *act)
{
if (act_argv_match(act, "help")) {
act_cmd_dev_help();
return 0;
} else if (act_argv_match(act, "show") ||
act_argv_match(act, "list") || act_no_arg(act)) {
act_arg_inc(act);
return act_cmd_dev_show(act);
} else if (act_argv_match(act, "move")) {
act_arg_inc(act);
return act_cmd_dev_move(act);
} else if (act_argv_match(act, "stop")) {
act_arg_inc(act);
return act_cmd_dev_stop(act);
}
pr_err("Command \"%s\" not found\n", act_argv(act));
return -ENOENT;
}
static void help(void)
{
pr_err("Usage: act [ OPTIONS ] OBJECT { COMMAND | help }\n"
"where OBJECT := { dev | monitor }\n"
" OPTIONS := { -V[ersion] }\n");
}
static int act_cmd(struct act *act)
{
if (act_argv_match(act, "help") || act_no_arg(act)) {
help();
return 0;
} else if (act_argv_match(act, "dev")) {
act_arg_inc(act);
return act_cmd_dev(act);
} else if (act_argv_match(act, "monitor")) {
act_arg_inc(act);
return act_cmd_mon(act);
}
pr_err("Object \"%s\" not found\n", act_argv(act));
return -ENOENT;
}
static int act_init(struct act *act, int argc, char **argv)
{
int err;
act->argc = argc;
act->argv = argv;
err = actuator_init(&act->actuator);
if (err) {
pr_err("Failed to connect to actuator Netlink\n");
return -errno;
}
return 0;
}
static void act_fini(struct act *act)
{
actuator_fini(act->actuator);
}
static struct act *act_alloc(void)
{
struct act *act;
act = calloc(1, sizeof(*act));
if (!act)
return NULL;
return act;
}
static void act_free(struct act *act)
{
free(act);
}
int main(int argc, char **argv)
{
static const struct option long_options[] = {
{ "Version", no_argument, NULL, 'V' },
{ NULL, 0, NULL, 0 }
};
struct act *act;
int opt;
int err;
int ret;
act = act_alloc();
if (!act) {
pr_err("Failed to allocate memory for context\n");
return EXIT_FAILURE;
}
while ((opt = getopt_long(argc, argv, "V",
long_options, NULL)) >= 0) {
switch (opt) {
case 'V':
printf("act "PACKAGE_VERSION"\n");
ret = EXIT_SUCCESS;
goto act_free;
default:
pr_err("Unknown option.\n");
help();
ret = EXIT_FAILURE;
goto act_free;
}
}
argc -= optind;
argv += optind;
err = act_init(act, argc, argv);
if (err) {
ret = EXIT_FAILURE;
goto act_free;
}
err = act_cmd(act);
if (err) {
ret = EXIT_FAILURE;
goto act_fini;
}
ret = EXIT_SUCCESS;
act_fini:
act_fini(act);
act_free:
act_free(act);
return ret;
}