// SPDX-License-Identifier: GPL-2.0-only /* * Counter Watch Events - Test various counter watch events in a userspace application * * Copyright (C) STMicroelectronics 2023 - All Rights Reserved * Author: Fabrice Gasnier . */ #include #include #include #include #include #include #include #include #include #include static struct counter_watch simple_watch[] = { { /* Component data: Count 0 count */ .component.type = COUNTER_COMPONENT_COUNT, .component.scope = COUNTER_SCOPE_COUNT, .component.parent = 0, /* Event type: overflow or underflow */ .event = COUNTER_EVENT_OVERFLOW_UNDERFLOW, /* Device event channel 0 */ .channel = 0, }, }; static const char * const counter_event_type_name[] = { "COUNTER_EVENT_OVERFLOW", "COUNTER_EVENT_UNDERFLOW", "COUNTER_EVENT_OVERFLOW_UNDERFLOW", "COUNTER_EVENT_THRESHOLD", "COUNTER_EVENT_INDEX", "COUNTER_EVENT_CHANGE_OF_STATE", "COUNTER_EVENT_CAPTURE", }; static const char * const counter_component_type_name[] = { "COUNTER_COMPONENT_NONE", "COUNTER_COMPONENT_SIGNAL", "COUNTER_COMPONENT_COUNT", "COUNTER_COMPONENT_FUNCTION", "COUNTER_COMPONENT_SYNAPSE_ACTION", "COUNTER_COMPONENT_EXTENSION", }; static const char * const counter_scope_name[] = { "COUNTER_SCOPE_DEVICE", "COUNTER_SCOPE_SIGNAL", "COUNTER_SCOPE_COUNT", }; static void print_watch(struct counter_watch *watch, int nwatch) { int i; /* prints the watch array in C-like structure */ printf("watch[%d] = {\n", nwatch); for (i = 0; i < nwatch; i++) { printf(" [%d] =\t{\n" "\t\t.component.type = %s\n" "\t\t.component.scope = %s\n" "\t\t.component.parent = %d\n" "\t\t.component.id = %d\n" "\t\t.event = %s\n" "\t\t.channel = %d\n" "\t},\n", i, counter_component_type_name[watch[i].component.type], counter_scope_name[watch[i].component.scope], watch[i].component.parent, watch[i].component.id, counter_event_type_name[watch[i].event], watch[i].channel); } printf("};\n"); } static void print_usage(void) { fprintf(stderr, "Usage:\n\n" "counter_watch_events [options] [-w ]\n" "counter_watch_events [options] [-w ] [-w ]...\n" "\n" "When no --watch option has been provided, simple watch example is used:\n" "counter_watch_events [options] -w comp_count,scope_count,evt_ovf_udf\n" "\n" "Test various watch events for given counter device.\n" "\n" "Options:\n" " -d, --debug Prints debug information\n" " -h, --help Prints usage\n" " -n, --device-num Use /dev/counter [default: /dev/counter0]\n" " -l, --loop Loop for events [default: 0 (forever)]\n" " -w, --watch comma-separated list of watch options\n" "\n" "Watch options:\n" " scope_device (COUNTER_SCOPE_DEVICE) [default: scope_device]\n" " scope_signal (COUNTER_SCOPE_SIGNAL)\n" " scope_count (COUNTER_SCOPE_COUNT)\n" "\n" " comp_none (COUNTER_COMPONENT_NONE) [default: comp_none]\n" " comp_signal (COUNTER_COMPONENT_SIGNAL)\n" " comp_count (COUNTER_COMPONENT_COUNT)\n" " comp_function (COUNTER_COMPONENT_FUNCTION)\n" " comp_synapse_action (COUNTER_COMPONENT_SYNAPSE_ACTION)\n" " comp_extension (COUNTER_COMPONENT_EXTENSION)\n" "\n" " evt_ovf (COUNTER_EVENT_OVERFLOW) [default: evt_ovf]\n" " evt_udf (COUNTER_EVENT_UNDERFLOW)\n" " evt_ovf_udf (COUNTER_EVENT_OVERFLOW_UNDERFLOW)\n" " evt_threshold (COUNTER_EVENT_THRESHOLD)\n" " evt_index (COUNTER_EVENT_INDEX)\n" " evt_change_of_state (COUNTER_EVENT_CHANGE_OF_STATE)\n" " evt_capture (COUNTER_EVENT_CAPTURE)\n" "\n" " chan= channel for this watch [default: 0]\n" " id= component id for this watch [default: 0]\n" " parent= component parent for this watch [default: 0]\n" "\n" "Example with two watched events:\n\n" "counter_watch_events -d \\\n" "\t-w comp_count,scope_count,evt_ovf_udf \\\n" "\t-w comp_extension,scope_count,evt_capture,id=7,chan=3\n" ); } static const struct option longopts[] = { { "debug", no_argument, 0, 'd' }, { "help", no_argument, 0, 'h' }, { "device-num", required_argument, 0, 'n' }, { "loop", required_argument, 0, 'l' }, { "watch", required_argument, 0, 'w' }, { }, }; /* counter watch subopts */ enum { WATCH_SCOPE_DEVICE, WATCH_SCOPE_SIGNAL, WATCH_SCOPE_COUNT, WATCH_COMPONENT_NONE, WATCH_COMPONENT_SIGNAL, WATCH_COMPONENT_COUNT, WATCH_COMPONENT_FUNCTION, WATCH_COMPONENT_SYNAPSE_ACTION, WATCH_COMPONENT_EXTENSION, WATCH_EVENT_OVERFLOW, WATCH_EVENT_UNDERFLOW, WATCH_EVENT_OVERFLOW_UNDERFLOW, WATCH_EVENT_THRESHOLD, WATCH_EVENT_INDEX, WATCH_EVENT_CHANGE_OF_STATE, WATCH_EVENT_CAPTURE, WATCH_CHANNEL, WATCH_ID, WATCH_PARENT, WATCH_SUBOPTS_MAX, }; static char * const counter_watch_subopts[WATCH_SUBOPTS_MAX + 1] = { /* component.scope */ [WATCH_SCOPE_DEVICE] = "scope_device", [WATCH_SCOPE_SIGNAL] = "scope_signal", [WATCH_SCOPE_COUNT] = "scope_count", /* component.type */ [WATCH_COMPONENT_NONE] = "comp_none", [WATCH_COMPONENT_SIGNAL] = "comp_signal", [WATCH_COMPONENT_COUNT] = "comp_count", [WATCH_COMPONENT_FUNCTION] = "comp_function", [WATCH_COMPONENT_SYNAPSE_ACTION] = "comp_synapse_action", [WATCH_COMPONENT_EXTENSION] = "comp_extension", /* event */ [WATCH_EVENT_OVERFLOW] = "evt_ovf", [WATCH_EVENT_UNDERFLOW] = "evt_udf", [WATCH_EVENT_OVERFLOW_UNDERFLOW] = "evt_ovf_udf", [WATCH_EVENT_THRESHOLD] = "evt_threshold", [WATCH_EVENT_INDEX] = "evt_index", [WATCH_EVENT_CHANGE_OF_STATE] = "evt_change_of_state", [WATCH_EVENT_CAPTURE] = "evt_capture", /* channel, id, parent */ [WATCH_CHANNEL] = "chan", [WATCH_ID] = "id", [WATCH_PARENT] = "parent", /* Empty entry ends the opts array */ NULL }; int main(int argc, char **argv) { int c, fd, i, ret, rc = 0, debug = 0, loop = 0, dev_num = 0, nwatch = 0; struct counter_event event_data; char *device_name = NULL, *subopts, *value; struct counter_watch *watches; /* * 1st pass: * - list watch events number to allocate the watch array. * - parse normal options (other than watch options) */ while ((c = getopt_long(argc, argv, "dhn:l:w:", longopts, NULL)) != -1) { switch (c) { case 'd': debug = 1; break; case 'h': print_usage(); return EXIT_SUCCESS; case 'n': dev_num = strtoul(optarg, NULL, 10); if (errno) { perror("strtol failed: --device-num \n"); return EXIT_FAILURE; } break; case 'l': loop = strtol(optarg, NULL, 10); if (errno) { perror("strtol failed: --loop \n"); return EXIT_FAILURE; } break; case 'w': nwatch++; break; default: return EXIT_FAILURE; } } if (nwatch) { watches = calloc(nwatch, sizeof(*watches)); if (!watches) { perror("Error allocating watches\n"); return EXIT_FAILURE; } } else { /* default to simple watch example */ watches = simple_watch; nwatch = ARRAY_SIZE(simple_watch); } /* 2nd pass: parse watch sub-options to fill in watch array */ optind = 1; i = 0; while ((c = getopt_long(argc, argv, "dhn:l:w:", longopts, NULL)) != -1) { switch (c) { case 'w': subopts = optarg; while (*subopts != '\0') { ret = getsubopt(&subopts, counter_watch_subopts, &value); switch (ret) { case WATCH_SCOPE_DEVICE: case WATCH_SCOPE_SIGNAL: case WATCH_SCOPE_COUNT: /* match with counter_scope */ watches[i].component.scope = ret; break; case WATCH_COMPONENT_NONE: case WATCH_COMPONENT_SIGNAL: case WATCH_COMPONENT_COUNT: case WATCH_COMPONENT_FUNCTION: case WATCH_COMPONENT_SYNAPSE_ACTION: case WATCH_COMPONENT_EXTENSION: /* match counter_component_type: subtract enum value */ ret -= WATCH_COMPONENT_NONE; watches[i].component.type = ret; break; case WATCH_EVENT_OVERFLOW: case WATCH_EVENT_UNDERFLOW: case WATCH_EVENT_OVERFLOW_UNDERFLOW: case WATCH_EVENT_THRESHOLD: case WATCH_EVENT_INDEX: case WATCH_EVENT_CHANGE_OF_STATE: case WATCH_EVENT_CAPTURE: /* match counter_event_type: subtract enum value */ ret -= WATCH_EVENT_OVERFLOW; watches[i].event = ret; break; case WATCH_CHANNEL: if (!value) { fprintf(stderr, "Invalid chan=\n"); rc = EXIT_FAILURE; goto err_free_watches; } watches[i].channel = strtoul(value, NULL, 10); if (errno) { perror("strtoul failed: chan=\n"); rc = EXIT_FAILURE; goto err_free_watches; } break; case WATCH_ID: if (!value) { fprintf(stderr, "Invalid id=\n"); rc = EXIT_FAILURE; goto err_free_watches; } watches[i].component.id = strtoul(value, NULL, 10); if (errno) { perror("strtoul failed: id=\n"); rc = EXIT_FAILURE; goto err_free_watches; } break; case WATCH_PARENT: if (!value) { fprintf(stderr, "Invalid parent=\n"); rc = EXIT_FAILURE; goto err_free_watches; } watches[i].component.parent = strtoul(value, NULL, 10); if (errno) { perror("strtoul failed: parent=\n"); rc = EXIT_FAILURE; goto err_free_watches; } break; default: fprintf(stderr, "Unknown suboption '%s'\n", value); rc = EXIT_FAILURE; goto err_free_watches; } } i++; break; } } if (debug) print_watch(watches, nwatch); ret = asprintf(&device_name, "/dev/counter%d", dev_num); if (ret < 0) { fprintf(stderr, "asprintf failed\n"); rc = EXIT_FAILURE; goto err_free_watches; } if (debug) printf("Opening %s\n", device_name); fd = open(device_name, O_RDWR); if (fd == -1) { fprintf(stderr, "Unable to open %s: %s\n", device_name, strerror(errno)); free(device_name); rc = EXIT_FAILURE; goto err_free_watches; } free(device_name); for (i = 0; i < nwatch; i++) { ret = ioctl(fd, COUNTER_ADD_WATCH_IOCTL, watches + i); if (ret == -1) { fprintf(stderr, "Error adding watches[%d]: %s\n", i, strerror(errno)); rc = EXIT_FAILURE; goto err_close; } } ret = ioctl(fd, COUNTER_ENABLE_EVENTS_IOCTL); if (ret == -1) { perror("Error enabling events"); rc = EXIT_FAILURE; goto err_close; } for (i = 0; loop <= 0 || i < loop; i++) { ret = read(fd, &event_data, sizeof(event_data)); if (ret == -1) { perror("Failed to read event data"); rc = EXIT_FAILURE; goto err_close; } if (ret != sizeof(event_data)) { fprintf(stderr, "Failed to read event data (got: %d)\n", ret); rc = EXIT_FAILURE; goto err_close; } printf("Timestamp: %llu\tData: %llu\t event: %s\tch: %d\n", event_data.timestamp, event_data.value, counter_event_type_name[event_data.watch.event], event_data.watch.channel); if (event_data.status) { fprintf(stderr, "Error %d: %s\n", event_data.status, strerror(event_data.status)); } } err_close: close(fd); err_free_watches: if (watches != simple_watch) free(watches); return rc; }