407 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Counter Watch Events - Test various counter watch events in a userspace application4 *5 * Copyright (C) STMicroelectronics 2023 - All Rights Reserved6 * Author: Fabrice Gasnier <fabrice.gasnier@foss.st.com>.7 */8 9#include <errno.h>10#include <fcntl.h>11#include <getopt.h>12#include <linux/counter.h>13#include <linux/kernel.h>14#include <stdlib.h>15#include <stdio.h>16#include <string.h>17#include <sys/ioctl.h>18#include <unistd.h>19 20static struct counter_watch simple_watch[] = {21 {22 /* Component data: Count 0 count */23 .component.type = COUNTER_COMPONENT_COUNT,24 .component.scope = COUNTER_SCOPE_COUNT,25 .component.parent = 0,26 /* Event type: overflow or underflow */27 .event = COUNTER_EVENT_OVERFLOW_UNDERFLOW,28 /* Device event channel 0 */29 .channel = 0,30 },31};32 33static const char * const counter_event_type_name[] = {34 "COUNTER_EVENT_OVERFLOW",35 "COUNTER_EVENT_UNDERFLOW",36 "COUNTER_EVENT_OVERFLOW_UNDERFLOW",37 "COUNTER_EVENT_THRESHOLD",38 "COUNTER_EVENT_INDEX",39 "COUNTER_EVENT_CHANGE_OF_STATE",40 "COUNTER_EVENT_CAPTURE",41};42 43static const char * const counter_component_type_name[] = {44 "COUNTER_COMPONENT_NONE",45 "COUNTER_COMPONENT_SIGNAL",46 "COUNTER_COMPONENT_COUNT",47 "COUNTER_COMPONENT_FUNCTION",48 "COUNTER_COMPONENT_SYNAPSE_ACTION",49 "COUNTER_COMPONENT_EXTENSION",50};51 52static const char * const counter_scope_name[] = {53 "COUNTER_SCOPE_DEVICE",54 "COUNTER_SCOPE_SIGNAL",55 "COUNTER_SCOPE_COUNT",56};57 58static void print_watch(struct counter_watch *watch, int nwatch)59{60 int i;61 62 /* prints the watch array in C-like structure */63 printf("watch[%d] = {\n", nwatch);64 for (i = 0; i < nwatch; i++) {65 printf(" [%d] =\t{\n"66 "\t\t.component.type = %s\n"67 "\t\t.component.scope = %s\n"68 "\t\t.component.parent = %d\n"69 "\t\t.component.id = %d\n"70 "\t\t.event = %s\n"71 "\t\t.channel = %d\n"72 "\t},\n",73 i,74 counter_component_type_name[watch[i].component.type],75 counter_scope_name[watch[i].component.scope],76 watch[i].component.parent,77 watch[i].component.id,78 counter_event_type_name[watch[i].event],79 watch[i].channel);80 }81 printf("};\n");82}83 84static void print_usage(void)85{86 fprintf(stderr, "Usage:\n\n"87 "counter_watch_events [options] [-w <watchoptions>]\n"88 "counter_watch_events [options] [-w <watch1 options>] [-w <watch2 options>]...\n"89 "\n"90 "When no --watch option has been provided, simple watch example is used:\n"91 "counter_watch_events [options] -w comp_count,scope_count,evt_ovf_udf\n"92 "\n"93 "Test various watch events for given counter device.\n"94 "\n"95 "Options:\n"96 " -d, --debug Prints debug information\n"97 " -h, --help Prints usage\n"98 " -n, --device-num <n> Use /dev/counter<n> [default: /dev/counter0]\n"99 " -l, --loop <n> Loop for <n> events [default: 0 (forever)]\n"100 " -w, --watch <watchoptions> comma-separated list of watch options\n"101 "\n"102 "Watch options:\n"103 " scope_device (COUNTER_SCOPE_DEVICE) [default: scope_device]\n"104 " scope_signal (COUNTER_SCOPE_SIGNAL)\n"105 " scope_count (COUNTER_SCOPE_COUNT)\n"106 "\n"107 " comp_none (COUNTER_COMPONENT_NONE) [default: comp_none]\n"108 " comp_signal (COUNTER_COMPONENT_SIGNAL)\n"109 " comp_count (COUNTER_COMPONENT_COUNT)\n"110 " comp_function (COUNTER_COMPONENT_FUNCTION)\n"111 " comp_synapse_action (COUNTER_COMPONENT_SYNAPSE_ACTION)\n"112 " comp_extension (COUNTER_COMPONENT_EXTENSION)\n"113 "\n"114 " evt_ovf (COUNTER_EVENT_OVERFLOW) [default: evt_ovf]\n"115 " evt_udf (COUNTER_EVENT_UNDERFLOW)\n"116 " evt_ovf_udf (COUNTER_EVENT_OVERFLOW_UNDERFLOW)\n"117 " evt_threshold (COUNTER_EVENT_THRESHOLD)\n"118 " evt_index (COUNTER_EVENT_INDEX)\n"119 " evt_change_of_state (COUNTER_EVENT_CHANGE_OF_STATE)\n"120 " evt_capture (COUNTER_EVENT_CAPTURE)\n"121 "\n"122 " chan=<n> channel <n> for this watch [default: 0]\n"123 " id=<n> component id <n> for this watch [default: 0]\n"124 " parent=<n> component parent <n> for this watch [default: 0]\n"125 "\n"126 "Example with two watched events:\n\n"127 "counter_watch_events -d \\\n"128 "\t-w comp_count,scope_count,evt_ovf_udf \\\n"129 "\t-w comp_extension,scope_count,evt_capture,id=7,chan=3\n"130 );131}132 133static const struct option longopts[] = {134 { "debug", no_argument, 0, 'd' },135 { "help", no_argument, 0, 'h' },136 { "device-num", required_argument, 0, 'n' },137 { "loop", required_argument, 0, 'l' },138 { "watch", required_argument, 0, 'w' },139 { },140};141 142/* counter watch subopts */143enum {144 WATCH_SCOPE_DEVICE,145 WATCH_SCOPE_SIGNAL,146 WATCH_SCOPE_COUNT,147 WATCH_COMPONENT_NONE,148 WATCH_COMPONENT_SIGNAL,149 WATCH_COMPONENT_COUNT,150 WATCH_COMPONENT_FUNCTION,151 WATCH_COMPONENT_SYNAPSE_ACTION,152 WATCH_COMPONENT_EXTENSION,153 WATCH_EVENT_OVERFLOW,154 WATCH_EVENT_UNDERFLOW,155 WATCH_EVENT_OVERFLOW_UNDERFLOW,156 WATCH_EVENT_THRESHOLD,157 WATCH_EVENT_INDEX,158 WATCH_EVENT_CHANGE_OF_STATE,159 WATCH_EVENT_CAPTURE,160 WATCH_CHANNEL,161 WATCH_ID,162 WATCH_PARENT,163 WATCH_SUBOPTS_MAX,164};165 166static char * const counter_watch_subopts[WATCH_SUBOPTS_MAX + 1] = {167 /* component.scope */168 [WATCH_SCOPE_DEVICE] = "scope_device",169 [WATCH_SCOPE_SIGNAL] = "scope_signal",170 [WATCH_SCOPE_COUNT] = "scope_count",171 /* component.type */172 [WATCH_COMPONENT_NONE] = "comp_none",173 [WATCH_COMPONENT_SIGNAL] = "comp_signal",174 [WATCH_COMPONENT_COUNT] = "comp_count",175 [WATCH_COMPONENT_FUNCTION] = "comp_function",176 [WATCH_COMPONENT_SYNAPSE_ACTION] = "comp_synapse_action",177 [WATCH_COMPONENT_EXTENSION] = "comp_extension",178 /* event */179 [WATCH_EVENT_OVERFLOW] = "evt_ovf",180 [WATCH_EVENT_UNDERFLOW] = "evt_udf",181 [WATCH_EVENT_OVERFLOW_UNDERFLOW] = "evt_ovf_udf",182 [WATCH_EVENT_THRESHOLD] = "evt_threshold",183 [WATCH_EVENT_INDEX] = "evt_index",184 [WATCH_EVENT_CHANGE_OF_STATE] = "evt_change_of_state",185 [WATCH_EVENT_CAPTURE] = "evt_capture",186 /* channel, id, parent */187 [WATCH_CHANNEL] = "chan",188 [WATCH_ID] = "id",189 [WATCH_PARENT] = "parent",190 /* Empty entry ends the opts array */191 NULL192};193 194int main(int argc, char **argv)195{196 int c, fd, i, ret, rc = 0, debug = 0, loop = 0, dev_num = 0, nwatch = 0;197 struct counter_event event_data;198 char *device_name = NULL, *subopts, *value;199 struct counter_watch *watches;200 201 /*202 * 1st pass:203 * - list watch events number to allocate the watch array.204 * - parse normal options (other than watch options)205 */206 while ((c = getopt_long(argc, argv, "dhn:l:w:", longopts, NULL)) != -1) {207 switch (c) {208 case 'd':209 debug = 1;210 break;211 case 'h':212 print_usage();213 return EXIT_SUCCESS;214 case 'n':215 dev_num = strtoul(optarg, NULL, 10);216 if (errno) {217 perror("strtol failed: --device-num <n>\n");218 return EXIT_FAILURE;219 }220 break;221 case 'l':222 loop = strtol(optarg, NULL, 10);223 if (errno) {224 perror("strtol failed: --loop <n>\n");225 return EXIT_FAILURE;226 }227 break;228 case 'w':229 nwatch++;230 break;231 default:232 return EXIT_FAILURE;233 }234 }235 236 if (nwatch) {237 watches = calloc(nwatch, sizeof(*watches));238 if (!watches) {239 perror("Error allocating watches\n");240 return EXIT_FAILURE;241 }242 } else {243 /* default to simple watch example */244 watches = simple_watch;245 nwatch = ARRAY_SIZE(simple_watch);246 }247 248 /* 2nd pass: parse watch sub-options to fill in watch array */249 optind = 1;250 i = 0;251 while ((c = getopt_long(argc, argv, "dhn:l:w:", longopts, NULL)) != -1) {252 switch (c) {253 case 'w':254 subopts = optarg;255 while (*subopts != '\0') {256 ret = getsubopt(&subopts, counter_watch_subopts, &value);257 switch (ret) {258 case WATCH_SCOPE_DEVICE:259 case WATCH_SCOPE_SIGNAL:260 case WATCH_SCOPE_COUNT:261 /* match with counter_scope */262 watches[i].component.scope = ret;263 break;264 case WATCH_COMPONENT_NONE:265 case WATCH_COMPONENT_SIGNAL:266 case WATCH_COMPONENT_COUNT:267 case WATCH_COMPONENT_FUNCTION:268 case WATCH_COMPONENT_SYNAPSE_ACTION:269 case WATCH_COMPONENT_EXTENSION:270 /* match counter_component_type: subtract enum value */271 ret -= WATCH_COMPONENT_NONE;272 watches[i].component.type = ret;273 break;274 case WATCH_EVENT_OVERFLOW:275 case WATCH_EVENT_UNDERFLOW:276 case WATCH_EVENT_OVERFLOW_UNDERFLOW:277 case WATCH_EVENT_THRESHOLD:278 case WATCH_EVENT_INDEX:279 case WATCH_EVENT_CHANGE_OF_STATE:280 case WATCH_EVENT_CAPTURE:281 /* match counter_event_type: subtract enum value */282 ret -= WATCH_EVENT_OVERFLOW;283 watches[i].event = ret;284 break;285 case WATCH_CHANNEL:286 if (!value) {287 fprintf(stderr, "Invalid chan=<number>\n");288 rc = EXIT_FAILURE;289 goto err_free_watches;290 }291 watches[i].channel = strtoul(value, NULL, 10);292 if (errno) {293 perror("strtoul failed: chan=<number>\n");294 rc = EXIT_FAILURE;295 goto err_free_watches;296 }297 break;298 case WATCH_ID:299 if (!value) {300 fprintf(stderr, "Invalid id=<number>\n");301 rc = EXIT_FAILURE;302 goto err_free_watches;303 }304 watches[i].component.id = strtoul(value, NULL, 10);305 if (errno) {306 perror("strtoul failed: id=<number>\n");307 rc = EXIT_FAILURE;308 goto err_free_watches;309 }310 break;311 case WATCH_PARENT:312 if (!value) {313 fprintf(stderr, "Invalid parent=<number>\n");314 rc = EXIT_FAILURE;315 goto err_free_watches;316 }317 watches[i].component.parent = strtoul(value, NULL, 10);318 if (errno) {319 perror("strtoul failed: parent=<number>\n");320 rc = EXIT_FAILURE;321 goto err_free_watches;322 }323 break;324 default:325 fprintf(stderr, "Unknown suboption '%s'\n", value);326 rc = EXIT_FAILURE;327 goto err_free_watches;328 }329 }330 i++;331 break;332 }333 }334 335 if (debug)336 print_watch(watches, nwatch);337 338 ret = asprintf(&device_name, "/dev/counter%d", dev_num);339 if (ret < 0) {340 fprintf(stderr, "asprintf failed\n");341 rc = EXIT_FAILURE;342 goto err_free_watches;343 }344 345 if (debug)346 printf("Opening %s\n", device_name);347 348 fd = open(device_name, O_RDWR);349 if (fd == -1) {350 fprintf(stderr, "Unable to open %s: %s\n", device_name, strerror(errno));351 free(device_name);352 rc = EXIT_FAILURE;353 goto err_free_watches;354 }355 free(device_name);356 357 for (i = 0; i < nwatch; i++) {358 ret = ioctl(fd, COUNTER_ADD_WATCH_IOCTL, watches + i);359 if (ret == -1) {360 fprintf(stderr, "Error adding watches[%d]: %s\n", i,361 strerror(errno));362 rc = EXIT_FAILURE;363 goto err_close;364 }365 }366 367 ret = ioctl(fd, COUNTER_ENABLE_EVENTS_IOCTL);368 if (ret == -1) {369 perror("Error enabling events");370 rc = EXIT_FAILURE;371 goto err_close;372 }373 374 for (i = 0; loop <= 0 || i < loop; i++) {375 ret = read(fd, &event_data, sizeof(event_data));376 if (ret == -1) {377 perror("Failed to read event data");378 rc = EXIT_FAILURE;379 goto err_close;380 }381 382 if (ret != sizeof(event_data)) {383 fprintf(stderr, "Failed to read event data (got: %d)\n", ret);384 rc = EXIT_FAILURE;385 goto err_close;386 }387 388 printf("Timestamp: %llu\tData: %llu\t event: %s\tch: %d\n",389 event_data.timestamp, event_data.value,390 counter_event_type_name[event_data.watch.event],391 event_data.watch.channel);392 393 if (event_data.status) {394 fprintf(stderr, "Error %d: %s\n", event_data.status,395 strerror(event_data.status));396 }397 }398 399err_close:400 close(fd);401err_free_watches:402 if (watches != simple_watch)403 free(watches);404 405 return rc;406}407