diff --git a/tools/hwsim.c b/tools/hwsim.c index 47a97be7..26c5f744 100644 --- a/tools/hwsim.c +++ b/tools/hwsim.c @@ -36,6 +36,7 @@ enum { HWSIM_CMD_TX_INFO_FRAME, HWSIM_CMD_NEW_RADIO, HWSIM_CMD_DEL_RADIO, + HWSIM_CMD_GET_RADIO, __HWSIM_CMD_MAX, }; #define HWSIM_CMD_MAX (_HWSIM_CMD_MAX - 1) @@ -58,6 +59,9 @@ enum { HWSIM_ATTR_SUPPORT_P2P_DEVICE, HWSIM_ATTR_USE_CHANCTX, HWSIM_ATTR_DESTROY_RADIO_ON_CLOSE, + HWSIM_ATTR_RADIO_NAME, + HWSIM_ATTR_NO_VIF, + HWSIM_ATTR_FREQ, __HWSIM_ATTR_MAX, }; #define HWSIM_ATTR_MAX (__HWSIM_ATTR_MAX - 1) @@ -66,6 +70,8 @@ static struct l_genl_family *hwsim; static bool keep_radios = false; static bool create_action = false; +static bool list_action = false; +static const char *list_option = NULL; static const char *destroy_action = NULL; static void do_debug(const char *str, void *user_data) @@ -147,6 +153,87 @@ static void hwsim_config(struct l_genl_msg *msg, void *user_data) } } +static void list_callback_done(void *user_data) +{ + l_main_quit(); +} + +static void list_callback(struct l_genl_msg *msg, void *user_data) +{ + struct l_genl_attr attr; + uint16_t type, len; + const void *data; + uint32_t idx = 0, channels = 0, custom_reg = 0; + bool reg_strict = false, p2p = false, chanctx = false; + char alpha2[2] = { }; + char *hwname = NULL; + + if (!l_genl_attr_init(&attr, msg)) { + int err = l_genl_msg_get_error(msg); + if (err < 0) { + l_warn("Failed to list radio [%d/%s]", + -err, strerror(-err)); + return; + } + } + + while (l_genl_attr_next(&attr, &type, &len, &data)) { + + switch (type) { + case HWSIM_ATTR_RADIO_ID: + if (len == 4) + idx = *(int *)data; + break; + + case HWSIM_ATTR_CHANNELS: + if (len == 4) + channels = *(uint32_t *)data; + break; + + case HWSIM_ATTR_REG_HINT_ALPHA2: + if (len == 2) + memcpy(&alpha2, data, len); + break; + + case HWSIM_ATTR_REG_CUSTOM_REG: + if (len == 4) + custom_reg = *(uint32_t *)data; + break; + + case HWSIM_ATTR_REG_STRICT_REG: + reg_strict = true; + break; + + case HWSIM_ATTR_SUPPORT_P2P_DEVICE: + p2p = true; + break; + + case HWSIM_ATTR_USE_CHANCTX: + chanctx = true; + break; + + case HWSIM_ATTR_RADIO_NAME: + hwname = l_malloc(len + 1); + if (hwname) { + strncpy(hwname, data, len); + hwname[len] = '\0'; + } + break; + + default: + break; + } + } + + printf("%s radio id %d channels %d alpha2 %d %d custom reg %d " + "reg strict %d p2p %d chanctx %d\n", + hwname, idx, channels, alpha2[0], alpha2[1], custom_reg, + reg_strict, p2p, chanctx); + + if (hwname) + l_free(hwname); +} + static void hwsim_ready(void *user_data) { struct l_genl_msg *msg; @@ -178,6 +265,21 @@ static void hwsim_ready(void *user_data) msg = l_genl_msg_new_sized(HWSIM_CMD_DEL_RADIO, 8); l_genl_msg_append_attr(msg, HWSIM_ATTR_RADIO_ID, 4, &id); l_genl_family_send(hwsim, msg, destroy_callback, NULL, NULL); + l_genl_msg_unref(msg); + } else if (list_action) { + msg = l_genl_msg_new_sized(HWSIM_CMD_GET_RADIO, + list_option ? 8: 4); + if (list_option) { + uint32_t id = atoi(list_option); + l_genl_msg_append_attr(msg, HWSIM_ATTR_RADIO_ID, + 4, &id); + l_genl_family_send(hwsim, msg, list_callback, + NULL, NULL); + } else { + l_genl_family_dump(hwsim, msg, list_callback, + NULL, list_callback_done); + } + l_genl_msg_unref(msg); } else l_main_quit(); @@ -206,6 +308,7 @@ static void usage(void) "Usage:\n"); printf("\thwsim [options]\n"); printf("Options:\n" + "\t-L, --list [id] List simulated radios\n" "\t-C, --create Create new simulated radio\n" "\t-D, --destroy Destroy existing radio\n" "\t-k, --keep Do not destroy radios when " @@ -214,6 +317,7 @@ static void usage(void) } static const struct option main_options[] = { + { "list", optional_argument, NULL, 'L' }, { "create", no_argument, NULL, 'C' }, { "destroy", required_argument, NULL, 'D' }, { "keep", no_argument, NULL, 'k' }, @@ -227,21 +331,37 @@ int main(int argc, char *argv[]) struct l_signal *signal; struct l_genl *genl; sigset_t mask; - int exit_status; + int exit_status, actions = 0; for (;;) { int opt; - opt = getopt_long(argc, argv, "CD:vhk", main_options, NULL); + opt = getopt_long(argc, argv, ":L:CD:vhk", main_options, NULL); if (opt < 0) break; switch (opt) { + case ':': + if (optopt == 'L') { + list_action = true; + actions++; + } else { + printf("option '-%c' requires an argument\n", + optopt); + } + break; + case 'L': + list_action = true; + list_option = optarg; + actions++; + break; case 'C': create_action = true; + actions++; break; case 'D': destroy_action = optarg; + actions++; break; case 'k': keep_radios = true; @@ -253,6 +373,8 @@ int main(int argc, char *argv[]) usage(); return EXIT_SUCCESS; default: + printf("unrecognized argument '%s'\n", + argv[optind - 1]); return EXIT_FAILURE; } } @@ -262,12 +384,12 @@ int main(int argc, char *argv[]) return EXIT_FAILURE; } - if (create_action && destroy_action) { + if (actions > 1) { fprintf(stderr, "Only one action can be specified\n"); return EXIT_FAILURE; } - if (!create_action && !destroy_action) { + if (!actions) { fprintf(stderr, "No action has been specified\n"); return EXIT_FAILURE; }