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