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 }; 42 43 static 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 52 static const char * const counter_scope_name[] = { 53 "COUNTER_SCOPE_DEVICE", 54 "COUNTER_SCOPE_SIGNAL", 55 "COUNTER_SCOPE_COUNT", 56 }; 57 58 static 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 84 static 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 133 static 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 */ 143 enum { 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 166 static 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 NULL 192 }; 193 194 int 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 399 err_close: 400 close(fd); 401 err_free_watches: 402 if (watches != simple_watch) 403 free(watches); 404 405 return rc; 406 } 407