1// SPDX-License-Identifier: GPL-2.0+ 2/* 3 * comedi/drivers/ni_routes.c 4 * Route information for NI boards. 5 * 6 * COMEDI - Linux Control and Measurement Device Interface 7 * Copyright (C) 2016 Spencer E. Olson <olsonse@umich.edu> 8 * 9 * This program is free software; you can redistribute it and/or modify 10 * it under the terms of the GNU General Public License as published by 11 * the Free Software Foundation; either version 2 of the License, or 12 * (at your option) any later version. 13 * 14 * This program is distributed in the hope that it will be useful, 15 * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 * GNU General Public License for more details. 18 */ 19 20#include <linux/module.h> 21#include <linux/slab.h> 22#include <linux/bsearch.h> 23#include <linux/sort.h> 24#include <linux/comedi.h> 25 26#include "ni_routes.h" 27#include "ni_routing/ni_route_values.h" 28#include "ni_routing/ni_device_routes.h" 29 30/* 31 * This is defined in ni_routing/ni_route_values.h: 32 * #define B(x) ((x) - NI_NAMES_BASE) 33 */ 34 35/* 36 * These are defined in ni_routing/ni_route_values.h to identify clearly 37 * elements of the table that were set. In other words, entries that are zero 38 * are invalid. To get the value to use for the register, one must mask out the 39 * high bit. 40 * 41 * #define V(x) ((x) | 0x80) 42 * 43 * #define UNMARK(x) ((x) & (~(0x80))) 44 * 45 */ 46 47/* Helper for accessing data. */ 48#define RVi(table, src, dest) ((table)[(dest) * NI_NUM_NAMES + (src)]) 49 50/* 51 * Find the route values for a device family. 52 */ 53static const u8 *ni_find_route_values(const char *device_family) 54{ 55 const u8 *rv = NULL; 56 int i; 57 58 for (i = 0; ni_all_route_values[i]; ++i) { 59 if (!strcmp(ni_all_route_values[i]->family, device_family)) { 60 rv = &ni_all_route_values[i]->register_values[0][0]; 61 break; 62 } 63 } 64 return rv; 65} 66 67/* 68 * Find the valid routes for a board. 69 */ 70static const struct ni_device_routes * 71ni_find_valid_routes(const char *board_name) 72{ 73 const struct ni_device_routes *dr = NULL; 74 int i; 75 76 for (i = 0; ni_device_routes_list[i]; ++i) { 77 if (!strcmp(ni_device_routes_list[i]->device, board_name)) { 78 dr = ni_device_routes_list[i]; 79 break; 80 } 81 } 82 return dr; 83} 84 85/* 86 * Find the proper route_values and ni_device_routes tables for this particular 87 * device. Possibly try an alternate board name if device routes not found 88 * for the actual board name. 89 * 90 * Return: -ENODATA if either was not found; 0 if both were found. 91 */ 92static int ni_find_device_routes(const char *device_family, 93 const char *board_name, 94 const char *alt_board_name, 95 struct ni_route_tables *tables) 96{ 97 const struct ni_device_routes *dr; 98 const u8 *rv; 99 100 /* First, find the register_values table for this device family */ 101 rv = ni_find_route_values(device_family); 102 103 /* Second, find the set of routes valid for this device. */ 104 dr = ni_find_valid_routes(board_name); 105 if (!dr && alt_board_name) 106 dr = ni_find_valid_routes(alt_board_name); 107 108 tables->route_values = rv; 109 tables->valid_routes = dr; 110 111 if (!rv || !dr) 112 return -ENODATA; 113 114 return 0; 115} 116 117/** 118 * ni_assign_device_routes() - Assign the proper lookup table for NI signal 119 * routing to the specified NI device. 120 * @device_family: Device family name (determines route values). 121 * @board_name: Board name (determines set of routes). 122 * @alt_board_name: Optional alternate board name to try on failure. 123 * @tables: Pointer to assigned routing information. 124 * 125 * Finds the route values for the device family and the set of valid routes 126 * for the board. If valid routes could not be found for the actual board 127 * name and an alternate board name has been specified, try that one. 128 * 129 * On failure, the assigned routing information may be partially filled 130 * (for example, with the route values but not the set of valid routes). 131 * 132 * Return: -ENODATA if assignment was not successful; 0 if successful. 133 */ 134int ni_assign_device_routes(const char *device_family, 135 const char *board_name, 136 const char *alt_board_name, 137 struct ni_route_tables *tables) 138{ 139 memset(tables, 0, sizeof(struct ni_route_tables)); 140 return ni_find_device_routes(device_family, board_name, alt_board_name, 141 tables); 142} 143EXPORT_SYMBOL_GPL(ni_assign_device_routes); 144 145/** 146 * ni_count_valid_routes() - Count the number of valid routes. 147 * @tables: Routing tables for which to count all valid routes. 148 */ 149unsigned int ni_count_valid_routes(const struct ni_route_tables *tables) 150{ 151 int total = 0; 152 int i; 153 154 for (i = 0; i < tables->valid_routes->n_route_sets; ++i) { 155 const struct ni_route_set *R = &tables->valid_routes->routes[i]; 156 int j; 157 158 for (j = 0; j < R->n_src; ++j) { 159 const int src = R->src[j]; 160 const int dest = R->dest; 161 const u8 *rv = tables->route_values; 162 163 if (RVi(rv, B(src), B(dest))) 164 /* direct routing is valid */ 165 ++total; 166 else if (channel_is_rtsi(dest) && 167 (RVi(rv, B(src), B(NI_RGOUT0)) || 168 RVi(rv, B(src), B(NI_RTSI_BRD(0))) || 169 RVi(rv, B(src), B(NI_RTSI_BRD(1))) || 170 RVi(rv, B(src), B(NI_RTSI_BRD(2))) || 171 RVi(rv, B(src), B(NI_RTSI_BRD(3))))) { 172 ++total; 173 } 174 } 175 } 176 return total; 177} 178EXPORT_SYMBOL_GPL(ni_count_valid_routes); 179 180/** 181 * ni_get_valid_routes() - Implements INSN_DEVICE_CONFIG_GET_ROUTES. 182 * @tables: pointer to relevant set of routing tables. 183 * @n_pairs: Number of pairs for which memory is allocated by the user. If 184 * the user specifies '0', only the number of available pairs is 185 * returned. 186 * @pair_data: Pointer to memory allocated to return pairs back to user. Each 187 * even, odd indexed member of this array will hold source, 188 * destination of a route pair respectively. 189 * 190 * Return: the number of valid routes if n_pairs == 0; otherwise, the number of 191 * valid routes copied. 192 */ 193unsigned int ni_get_valid_routes(const struct ni_route_tables *tables, 194 unsigned int n_pairs, 195 unsigned int *pair_data) 196{ 197 unsigned int n_valid = ni_count_valid_routes(tables); 198 int i; 199 200 if (n_pairs == 0 || n_valid == 0) 201 return n_valid; 202 203 if (!pair_data) 204 return 0; 205 206 n_valid = 0; 207 208 for (i = 0; i < tables->valid_routes->n_route_sets; ++i) { 209 const struct ni_route_set *R = &tables->valid_routes->routes[i]; 210 int j; 211 212 for (j = 0; j < R->n_src; ++j) { 213 const int src = R->src[j]; 214 const int dest = R->dest; 215 bool valid = false; 216 const u8 *rv = tables->route_values; 217 218 if (RVi(rv, B(src), B(dest))) 219 /* direct routing is valid */ 220 valid = true; 221 else if (channel_is_rtsi(dest) && 222 (RVi(rv, B(src), B(NI_RGOUT0)) || 223 RVi(rv, B(src), B(NI_RTSI_BRD(0))) || 224 RVi(rv, B(src), B(NI_RTSI_BRD(1))) || 225 RVi(rv, B(src), B(NI_RTSI_BRD(2))) || 226 RVi(rv, B(src), B(NI_RTSI_BRD(3))))) { 227 /* indirect routing also valid */ 228 valid = true; 229 } 230 231 if (valid) { 232 pair_data[2 * n_valid] = src; 233 pair_data[2 * n_valid + 1] = dest; 234 ++n_valid; 235 } 236 237 if (n_valid >= n_pairs) 238 return n_valid; 239 } 240 } 241 return n_valid; 242} 243EXPORT_SYMBOL_GPL(ni_get_valid_routes); 244 245/* 246 * List of NI global signal names that, as destinations, are only routeable 247 * indirectly through the *_arg elements of the comedi_cmd structure. 248 */ 249static const int NI_CMD_DESTS[] = { 250 NI_AI_SampleClock, 251 NI_AI_StartTrigger, 252 NI_AI_ConvertClock, 253 NI_AO_SampleClock, 254 NI_AO_StartTrigger, 255 NI_DI_SampleClock, 256 NI_DO_SampleClock, 257}; 258 259/** 260 * ni_is_cmd_dest() - Determine whether the given destination is only 261 * configurable via a comedi_cmd struct. 262 * @dest: Destination to test. 263 */ 264bool ni_is_cmd_dest(int dest) 265{ 266 int i; 267 268 for (i = 0; i < ARRAY_SIZE(NI_CMD_DESTS); ++i) 269 if (NI_CMD_DESTS[i] == dest) 270 return true; 271 return false; 272} 273EXPORT_SYMBOL_GPL(ni_is_cmd_dest); 274 275/* **** BEGIN Routes sort routines **** */ 276static int _ni_sort_destcmp(const void *va, const void *vb) 277{ 278 const struct ni_route_set *a = va; 279 const struct ni_route_set *b = vb; 280 281 if (a->dest < b->dest) 282 return -1; 283 else if (a->dest > b->dest) 284 return 1; 285 return 0; 286} 287 288static int _ni_sort_srccmp(const void *vsrc0, const void *vsrc1) 289{ 290 const int *src0 = vsrc0; 291 const int *src1 = vsrc1; 292 293 if (*src0 < *src1) 294 return -1; 295 else if (*src0 > *src1) 296 return 1; 297 return 0; 298} 299 300/** 301 * ni_sort_device_routes() - Sort the list of valid device signal routes in 302 * preparation for use. 303 * @valid_routes: pointer to ni_device_routes struct to sort. 304 */ 305void ni_sort_device_routes(struct ni_device_routes *valid_routes) 306{ 307 unsigned int n; 308 309 /* 1. Count and set the number of ni_route_set objects. */ 310 valid_routes->n_route_sets = 0; 311 while (valid_routes->routes[valid_routes->n_route_sets].dest != 0) 312 ++valid_routes->n_route_sets; 313 314 /* 2. sort all ni_route_set objects by destination. */ 315 sort(valid_routes->routes, valid_routes->n_route_sets, 316 sizeof(struct ni_route_set), _ni_sort_destcmp, NULL); 317 318 /* 3. Loop through each route_set for sorting. */ 319 for (n = 0; n < valid_routes->n_route_sets; ++n) { 320 struct ni_route_set *rs = &valid_routes->routes[n]; 321 322 /* 3a. Count and set the number of sources. */ 323 rs->n_src = 0; 324 while (rs->src[rs->n_src]) 325 ++rs->n_src; 326 327 /* 3a. Sort sources. */ 328 sort(valid_routes->routes[n].src, valid_routes->routes[n].n_src, 329 sizeof(int), _ni_sort_srccmp, NULL); 330 } 331} 332EXPORT_SYMBOL_GPL(ni_sort_device_routes); 333 334/* sort all valid device signal routes in prep for use */ 335static void ni_sort_all_device_routes(void) 336{ 337 unsigned int i; 338 339 for (i = 0; ni_device_routes_list[i]; ++i) 340 ni_sort_device_routes(ni_device_routes_list[i]); 341} 342 343/* **** BEGIN Routes search routines **** */ 344static int _ni_bsearch_destcmp(const void *vkey, const void *velt) 345{ 346 const int *key = vkey; 347 const struct ni_route_set *elt = velt; 348 349 if (*key < elt->dest) 350 return -1; 351 else if (*key > elt->dest) 352 return 1; 353 return 0; 354} 355 356static int _ni_bsearch_srccmp(const void *vkey, const void *velt) 357{ 358 const int *key = vkey; 359 const int *elt = velt; 360 361 if (*key < *elt) 362 return -1; 363 else if (*key > *elt) 364 return 1; 365 return 0; 366} 367 368/** 369 * ni_find_route_set() - Finds the proper route set with the specified 370 * destination. 371 * @destination: Destination of which to search for the route set. 372 * @valid_routes: Pointer to device routes within which to search. 373 * 374 * Return: NULL if no route_set is found with the specified @destination; 375 * otherwise, a pointer to the route_set if found. 376 */ 377const struct ni_route_set * 378ni_find_route_set(const int destination, 379 const struct ni_device_routes *valid_routes) 380{ 381 return bsearch(&destination, valid_routes->routes, 382 valid_routes->n_route_sets, sizeof(struct ni_route_set), 383 _ni_bsearch_destcmp); 384} 385EXPORT_SYMBOL_GPL(ni_find_route_set); 386 387/* 388 * ni_route_set_has_source() - Determines whether the given source is in 389 * included given route_set. 390 * 391 * Return: true if found; false otherwise. 392 */ 393bool ni_route_set_has_source(const struct ni_route_set *routes, 394 const int source) 395{ 396 if (!bsearch(&source, routes->src, routes->n_src, sizeof(int), 397 _ni_bsearch_srccmp)) 398 return false; 399 return true; 400} 401EXPORT_SYMBOL_GPL(ni_route_set_has_source); 402 403/** 404 * ni_lookup_route_register() - Look up a register value for a particular route 405 * without checking whether the route is valid for 406 * the particular device. 407 * @src: global-identifier for route source 408 * @dest: global-identifier for route destination 409 * @tables: pointer to relevant set of routing tables. 410 * 411 * Return: -EINVAL if the specified route is not valid for this device family. 412 */ 413s8 ni_lookup_route_register(int src, int dest, 414 const struct ni_route_tables *tables) 415{ 416 s8 regval; 417 418 /* 419 * Be sure to use the B() macro to subtract off the NI_NAMES_BASE before 420 * indexing into the route_values array. 421 */ 422 src = B(src); 423 dest = B(dest); 424 if (src < 0 || src >= NI_NUM_NAMES || dest < 0 || dest >= NI_NUM_NAMES) 425 return -EINVAL; 426 regval = RVi(tables->route_values, src, dest); 427 if (!regval) 428 return -EINVAL; 429 /* mask out the valid-value marking bit */ 430 return UNMARK(regval); 431} 432EXPORT_SYMBOL_GPL(ni_lookup_route_register); 433 434/** 435 * ni_route_to_register() - Validates and converts the specified signal route 436 * (src-->dest) to the value used at the appropriate 437 * register. 438 * @src: global-identifier for route source 439 * @dest: global-identifier for route destination 440 * @tables: pointer to relevant set of routing tables. 441 * 442 * Generally speaking, most routes require the first six bits and a few require 443 * 7 bits. Special handling is given for the return value when the route is to 444 * be handled by the RTSI sub-device. In this case, the returned register may 445 * not be sufficient to define the entire route path, but rather may only 446 * indicate the intermediate route. For example, if the route must go through 447 * the RGOUT0 pin, the (src->RGOUT0) register value will be returned. 448 * Similarly, if the route must go through the NI_RTSI_BRD lines, the BIT(6) 449 * will be set: 450 * 451 * if route does not need RTSI_BRD lines: 452 * bits 0:7 : register value 453 * for a route that must go through RGOUT0 pin, this will be equal 454 * to the (src->RGOUT0) register value. 455 * else: * route is (src->RTSI_BRD(x), RTSI_BRD(x)->TRIGGER_LINE(i)) * 456 * bits 0:5 : zero 457 * bits 6 : set to 1 458 * bits 7:7 : zero 459 * 460 * Return: register value to be used for source at destination with special 461 * cases given above; Otherwise, -1 if the specified route is not valid for 462 * this particular device. 463 */ 464s8 ni_route_to_register(const int src, const int dest, 465 const struct ni_route_tables *tables) 466{ 467 const struct ni_route_set *routes = 468 ni_find_route_set(dest, tables->valid_routes); 469 const u8 *rv; 470 s8 regval; 471 472 /* first check to see if source is listed with bunch of destinations. */ 473 if (!routes) 474 return -1; 475 /* 2nd, check to see if destination is in list of source's targets. */ 476 if (!ni_route_set_has_source(routes, src)) 477 return -1; 478 /* 479 * finally, check to see if we know how to route... 480 * Be sure to use the B() macro to subtract off the NI_NAMES_BASE before 481 * indexing into the route_values array. 482 */ 483 rv = tables->route_values; 484 regval = RVi(rv, B(src), B(dest)); 485 486 /* 487 * if we did not validate the route, we'll see if we can route through 488 * one of the muxes 489 */ 490 if (!regval && channel_is_rtsi(dest)) { 491 regval = RVi(rv, B(src), B(NI_RGOUT0)); 492 if (!regval && (RVi(rv, B(src), B(NI_RTSI_BRD(0))) || 493 RVi(rv, B(src), B(NI_RTSI_BRD(1))) || 494 RVi(rv, B(src), B(NI_RTSI_BRD(2))) || 495 RVi(rv, B(src), B(NI_RTSI_BRD(3))))) 496 regval = BIT(6); 497 } 498 499 if (!regval) 500 return -1; 501 /* mask out the valid-value marking bit */ 502 return UNMARK(regval); 503} 504EXPORT_SYMBOL_GPL(ni_route_to_register); 505 506/* 507 * ni_find_route_source() - Finds the signal source corresponding to a signal 508 * route (src-->dest) of the specified routing register 509 * value and the specified route destination on the 510 * specified device. 511 * 512 * Note that this function does _not_ validate the source based on device 513 * routes. 514 * 515 * Return: The NI signal value (e.g. NI_PFI(0) or PXI_Clk10) if found. 516 * If the source was not found (i.e. the register value is not 517 * valid for any routes to the destination), -EINVAL is returned. 518 */ 519int ni_find_route_source(const u8 src_sel_reg_value, int dest, 520 const struct ni_route_tables *tables) 521{ 522 int src; 523 524 if (!tables->route_values) 525 return -EINVAL; 526 527 dest = B(dest); /* subtract NI names offset */ 528 /* ensure we are not going to under/over run the route value table */ 529 if (dest < 0 || dest >= NI_NUM_NAMES) 530 return -EINVAL; 531 for (src = 0; src < NI_NUM_NAMES; ++src) 532 if (RVi(tables->route_values, src, dest) == 533 V(src_sel_reg_value)) 534 return src + NI_NAMES_BASE; 535 return -EINVAL; 536} 537EXPORT_SYMBOL_GPL(ni_find_route_source); 538 539/* **** END Routes search routines **** */ 540 541/* **** BEGIN simple module entry/exit functions **** */ 542static int __init ni_routes_module_init(void) 543{ 544 ni_sort_all_device_routes(); 545 return 0; 546} 547 548static void __exit ni_routes_module_exit(void) 549{ 550} 551 552module_init(ni_routes_module_init); 553module_exit(ni_routes_module_exit); 554 555MODULE_AUTHOR("Comedi https://www.comedi.org"); 556MODULE_DESCRIPTION("Comedi helper for routing signals-->terminals for NI"); 557MODULE_LICENSE("GPL"); 558/* **** END simple module entry/exit functions **** */ 559