| /* |
| * 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; |
| } |