1 /* $NetBSD: if_media.c,v 1.54 2022/09/03 02:47:59 thorpej Exp $ */ 2 3 /*- 4 * Copyright (c) 1998, 2020 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility, 9 * NASA Ames Research Center. 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted provided that the following conditions 13 * are met: 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions and the following disclaimer. 16 * 2. Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in the 18 * documentation and/or other materials provided with the distribution. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 21 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 22 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 23 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 24 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 25 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 26 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 27 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 28 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 29 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 30 * POSSIBILITY OF SUCH DAMAGE. 31 */ 32 33 /* 34 * Copyright (c) 1997 35 * Jonathan Stone and Jason R. Thorpe. All rights reserved. 36 * 37 * This software is derived from information provided by Matt Thomas. 38 * 39 * Redistribution and use in source and binary forms, with or without 40 * modification, are permitted provided that the following conditions 41 * are met: 42 * 1. Redistributions of source code must retain the above copyright 43 * notice, this list of conditions and the following disclaimer. 44 * 2. Redistributions in binary form must reproduce the above copyright 45 * notice, this list of conditions and the following disclaimer in the 46 * documentation and/or other materials provided with the distribution. 47 * 3. All advertising materials mentioning features or use of this software 48 * must display the following acknowledgement: 49 * This product includes software developed by Jonathan Stone 50 * and Jason R. Thorpe for the NetBSD Project. 51 * 4. The names of the authors may not be used to endorse or promote products 52 * derived from this software without specific prior written permission. 53 * 54 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR 55 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 56 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 57 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 58 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 59 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 60 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 61 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 62 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 63 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 64 * SUCH DAMAGE. 65 */ 66 67 /* 68 * BSD/OS-compatible network interface media selection. 69 * 70 * Where it is safe to do so, this code strays slightly from the BSD/OS 71 * design. Software which uses the API (device drivers, basically) 72 * shouldn't notice any difference. 73 * 74 * Many thanks to Matt Thomas for providing the information necessary 75 * to implement this interface. 76 */ 77 78 #include <sys/cdefs.h> 79 __KERNEL_RCSID(0, "$NetBSD: if_media.c,v 1.54 2022/09/03 02:47:59 thorpej Exp $"); 80 81 #define __IFMEDIA_PRIVATE 82 83 #include <sys/param.h> 84 #include <sys/systm.h> 85 #include <sys/errno.h> 86 #include <sys/ioctl.h> 87 #include <sys/socket.h> 88 #include <sys/kmem.h> 89 90 #include <net/if.h> 91 #include <net/if_media.h> 92 93 static void ifmedia_status(struct ifmedia *, struct ifnet *, 94 struct ifmediareq *); 95 static struct ifmedia_entry * 96 ifmedia_match_locked(struct ifmedia *, u_int, u_int); 97 98 /* 99 * Compile-time options: 100 * IFMEDIA_DEBUG: 101 * Turn on implementation-level debug printfs. 102 * Useful for debugging newly-ported drivers. 103 */ 104 105 #ifdef IFMEDIA_DEBUG 106 int ifmedia_debug = 0; 107 static void ifmedia_printword(int); 108 #endif 109 110 /* 111 * We need to implement a recursive mutex to handle the un-converted 112 * driver case. For a fully MP-safe driver, the media lock will be 113 * held before calling any of the entry points that require it. However, 114 * this is not necessarily the case for a driver that hasn't yet been 115 * converted, and the entry point calls may be nested (for example 116 * mii_ifmedia_change -> ether_mediachange -> mii_mediachg). Luckily, 117 * the nesting won't be very deep, and 4 nested holds should be plenty. 118 */ 119 #define IFM_L_OWNLOCK 0x01 120 #define IFM_L_COUNT_MASK 0x3UL 121 #define IFM_L_CPU_MASK ~(IFM_L_COUNT_MASK) 122 123 void 124 ifmedia_lock_for_legacy(struct ifmedia *ifm) 125 { 126 uintptr_t cnt = IFM_L_OWNLOCK; 127 uintptr_t ci; 128 129 if (mutex_tryenter(ifm->ifm_lock)) { 130 goto gotit; 131 } 132 133 kpreempt_disable(); 134 ci = (uintptr_t)curcpu(); 135 if ((ifm->ifm_legacy & IFM_L_CPU_MASK) == ci) { 136 cnt = ifm->ifm_legacy & IFM_L_COUNT_MASK; 137 KASSERT(cnt < IFM_L_COUNT_MASK); 138 cnt++; 139 kpreempt_enable(); 140 goto gotit; 141 } 142 kpreempt_enable(); 143 144 mutex_enter(ifm->ifm_lock); 145 gotit: 146 KASSERT(kpreempt_disabled()); 147 ci = (uintptr_t)curcpu(); 148 KASSERT((ci & IFM_L_CPU_MASK) == ci); 149 ifm->ifm_legacy = ci | cnt; 150 } 151 152 void 153 ifmedia_unlock_for_legacy(struct ifmedia *ifm) 154 { 155 uintptr_t cnt; 156 uintptr_t ci = (uintptr_t)curcpu(); 157 158 KASSERT(kpreempt_disabled()); 159 KASSERT((ifm->ifm_legacy & IFM_L_CPU_MASK) == ci); 160 cnt = ifm->ifm_legacy & IFM_L_COUNT_MASK; 161 KASSERT(cnt != 0); 162 if (cnt == IFM_L_OWNLOCK) { 163 ifm->ifm_legacy = IFM_L_OWNLOCK; 164 mutex_exit(ifm->ifm_lock); 165 return; 166 } 167 cnt--; 168 ifm->ifm_legacy = ci | cnt; 169 } 170 171 /* 172 * Initialize if_media struct for a specific interface instance. 173 */ 174 void 175 ifmedia_init_with_lock(struct ifmedia *ifm, int dontcare_mask, 176 ifm_change_cb_t change_callback, ifm_stat_cb_t status_callback, 177 kmutex_t *lock) 178 { 179 180 /* 181 * XXX Would really like to assert: 182 * 183 * !if_is_mpsafe(ifp) || ((if_is_mpsafe(ifp) && lock != NULL) 184 * 185 * ...but we don't have access to the ifnet here. 186 */ 187 188 TAILQ_INIT(&ifm->ifm_list); 189 ifm->ifm_cur = NULL; 190 ifm->ifm_media = IFM_NONE; 191 ifm->ifm_mask = dontcare_mask; /* IF don't-care bits */ 192 ifm->ifm_change = change_callback; 193 ifm->ifm_status = status_callback; 194 ifm->ifm_legacy = 0; 195 196 if (lock == NULL) { 197 /* 198 * This is to support drivers that are not yet MP-safe 199 * with regard to the ifmedia layer. In these cases, 200 * we supply the lock and we ensure it's taken upon entry 201 * to various routines that expect it to be held. When 202 * we do this, we expect that the driver is in general a 203 * non-MP-safe driver and has already gone to splnet(). 204 */ 205 lock = mutex_obj_alloc(MUTEX_DEFAULT, IPL_NET); 206 ifm->ifm_legacy = IFM_L_OWNLOCK; 207 } 208 ifm->ifm_lock = lock; 209 } 210 211 void 212 ifmedia_init(struct ifmedia *ifm, int dontcare_mask, 213 ifm_change_cb_t change_callback, ifm_stat_cb_t status_callback) 214 { 215 ifmedia_init_with_lock(ifm, dontcare_mask, change_callback, 216 status_callback, NULL); 217 } 218 219 /* 220 * Free resources associated with an ifmedia. 221 */ 222 void 223 ifmedia_fini(struct ifmedia *ifm) 224 { 225 226 ifmedia_removeall(ifm); 227 228 if (ifm->ifm_legacy) { 229 KASSERT(ifm->ifm_legacy == IFM_L_OWNLOCK); 230 mutex_obj_free(ifm->ifm_lock); 231 } 232 ifm->ifm_legacy = 0; 233 ifm->ifm_lock = NULL; 234 } 235 236 int 237 ifmedia_change(struct ifmedia *ifm, struct ifnet *ifp) 238 { 239 int rv; 240 241 IFMEDIA_LOCK_FOR_LEGACY(ifm); 242 KASSERT(ifmedia_locked(ifm)); 243 if (ifm->ifm_change) 244 rv = (*ifm->ifm_change)(ifp); 245 else 246 rv = -1; 247 IFMEDIA_UNLOCK_FOR_LEGACY(ifm); 248 249 return rv; 250 } 251 252 static void 253 ifmedia_status(struct ifmedia *ifm, struct ifnet *ifp, struct ifmediareq *ifmr) 254 { 255 256 KASSERT(ifmedia_locked(ifm)); 257 if (ifm->ifm_status == NULL) 258 return; 259 (*ifm->ifm_status)(ifp, ifmr); 260 } 261 262 /* 263 * Add a media configuration to the list of supported media 264 * for a specific interface instance. 265 */ 266 static void 267 ifmedia_add_entry(struct ifmedia *ifm, int mword, int data, void *aux, 268 struct ifmedia_entry *entry) 269 { 270 271 #ifdef IFMEDIA_DEBUG 272 if (ifmedia_debug) { 273 if (ifm == NULL) { 274 printf("ifmedia_add: null ifm\n"); 275 return; 276 } 277 printf("Adding entry for "); 278 ifmedia_printword(mword); 279 } 280 #endif 281 282 entry->ifm_media = mword; 283 entry->ifm_data = data; 284 entry->ifm_aux = aux; 285 TAILQ_INSERT_TAIL(&ifm->ifm_list, entry, ifm_list); 286 } 287 288 void 289 ifmedia_add(struct ifmedia *ifm, int mword, int data, void *aux) 290 { 291 struct ifmedia_entry *entry; 292 293 entry = kmem_zalloc(sizeof(*entry), KM_SLEEP); 294 ifmedia_lock(ifm); 295 ifmedia_add_entry(ifm, mword, data, aux, entry); 296 ifmedia_unlock(ifm); 297 } 298 299 /* 300 * Add an array of media configurations to the list of 301 * supported media for a specific interface instance. 302 */ 303 void 304 ifmedia_list_add(struct ifmedia *ifm, struct ifmedia_entry *lp, int count) 305 { 306 int i; 307 308 for (i = 0; i < count; i++) 309 ifmedia_add(ifm, lp[i].ifm_media, lp[i].ifm_data, 310 lp[i].ifm_aux); 311 } 312 313 /* 314 * Set the default active media. 315 * 316 * Called by device-specific code which is assumed to have already 317 * selected the default media in hardware. We do _not_ call the 318 * media-change callback. 319 */ 320 void 321 ifmedia_set(struct ifmedia *ifm, int target) 322 { 323 struct ifmedia_entry *match, *entry = NULL; 324 325 ifmedia_lock(ifm); 326 match = ifmedia_match_locked(ifm, target, ifm->ifm_mask); 327 328 /* 329 * If we didn't find the requested media, then we try to fall 330 * back to target-type (IFM_ETHER, e.g.) | IFM_NONE. If that's 331 * not on the list, then we add it and set the media to it. 332 * 333 * Since ifmedia_set is almost always called with IFM_AUTO or 334 * with a known-good media, this really should only occur if we: 335 * 336 * a) didn't find any PHYs, or 337 * b) didn't find an autoselect option on the PHY when the 338 * parent ethernet driver expected to. 339 * 340 * In either case, it makes sense to select no media. 341 */ 342 if (match == NULL) { 343 printf("ifmedia_set: no match for 0x%x/0x%x\n", 344 target, ~ifm->ifm_mask); 345 target = (target & IFM_NMASK) | IFM_NONE; 346 match = ifmedia_match_locked(ifm, target, ifm->ifm_mask); 347 if (match == NULL) { 348 ifmedia_unlock(ifm); 349 entry = kmem_zalloc(sizeof(*entry), KM_SLEEP); 350 ifmedia_lock(ifm); 351 match = ifmedia_match_locked(ifm, target, 352 ifm->ifm_mask); 353 if (match == NULL) { 354 ifmedia_add_entry(ifm, target, 0, NULL, entry); 355 entry = NULL; 356 } 357 match = ifmedia_match_locked(ifm, target, 358 ifm->ifm_mask); 359 if (match == NULL) 360 panic("ifmedia_set failed"); 361 } 362 } 363 ifm->ifm_cur = match; 364 ifmedia_unlock(ifm); 365 366 if (entry) 367 kmem_free(entry, sizeof(*entry)); 368 369 #ifdef IFMEDIA_DEBUG 370 if (ifmedia_debug) { 371 printf("ifmedia_set: target "); 372 ifmedia_printword(target); 373 printf("ifmedia_set: setting to "); 374 ifmedia_printword(ifm->ifm_cur->ifm_media); 375 } 376 #endif 377 } 378 379 static int 380 ifmedia_getwords(struct ifmedia * const ifm, int *words, int maxwords) 381 { 382 struct ifmedia_entry *ep; 383 int nwords = 0; 384 385 KASSERT(ifmedia_locked(ifm)); 386 387 TAILQ_FOREACH(ep, &ifm->ifm_list, ifm_list) { 388 if (words != NULL && nwords < maxwords) { 389 words[nwords] = ep->ifm_media; 390 } 391 nwords++; 392 } 393 394 return nwords; 395 } 396 397 #define IFMEDIA_IOCTL_LOCK(ifm) \ 398 do { \ 399 if (ifmedia_islegacy(ifm)) \ 400 ifmedia_lock_for_legacy(ifm); \ 401 else \ 402 ifmedia_lock(ifm); \ 403 } while (/*CONSTCOND*/0) 404 405 #define IFMEDIA_IOCTL_UNLOCK(ifm) \ 406 do { \ 407 if (ifmedia_islegacy(ifm)) \ 408 ifmedia_unlock_for_legacy(ifm); \ 409 else \ 410 ifmedia_unlock(ifm); \ 411 } while (/*CONSTCOND*/0) 412 413 /* 414 * Device-independent media ioctl support function. 415 */ 416 int 417 ifmedia_ioctl(struct ifnet *ifp, struct ifreq *ifr, struct ifmedia *ifm, 418 u_long cmd) 419 { 420 struct ifmedia_entry *match; 421 struct ifmediareq *ifmr = (struct ifmediareq *)ifr; 422 int error = 0; 423 424 if (ifp == NULL || ifr == NULL || ifm == NULL) 425 return EINVAL; 426 427 KERNEL_LOCK_UNLESS_IFP_MPSAFE(ifp); 428 429 switch (cmd) { 430 case SIOCSIFMEDIA: /* Set the current media. */ 431 { 432 struct ifmedia_entry *oldentry; 433 u_int oldmedia; 434 u_int newmedia = ifr->ifr_media; 435 436 IFMEDIA_IOCTL_LOCK(ifm); 437 438 match = ifmedia_match_locked(ifm, newmedia, ifm->ifm_mask); 439 if (match == NULL) { 440 #ifdef IFMEDIA_DEBUG 441 if (ifmedia_debug) { 442 printf("ifmedia_ioctl: no media found for " 443 "0x%08x\n", newmedia); 444 } 445 #endif 446 IFMEDIA_IOCTL_UNLOCK(ifm); 447 error = EINVAL; 448 break; 449 } 450 451 /* 452 * If no change, we're done. 453 * XXX Automedia may involve software intervention. 454 * Keep going in case the connected media changed. 455 * Similarly, if best match changed (kernel debugger?). 456 */ 457 if ((IFM_SUBTYPE(newmedia) != IFM_AUTO) && 458 (newmedia == ifm->ifm_media) && (match == ifm->ifm_cur)) { 459 IFMEDIA_IOCTL_UNLOCK(ifm); 460 break; 461 } 462 463 /* 464 * We found a match, now make the driver switch to it. 465 * Make sure to preserve our old media type in case the 466 * driver can't switch. 467 */ 468 #ifdef IFMEDIA_DEBUG 469 if (ifmedia_debug) { 470 printf("ifmedia_ioctl: switching %s to ", 471 ifp->if_xname); 472 ifmedia_printword(match->ifm_media); 473 } 474 #endif 475 oldentry = ifm->ifm_cur; 476 oldmedia = ifm->ifm_media; 477 ifm->ifm_cur = match; 478 ifm->ifm_media = newmedia; 479 error = ifmedia_change(ifm, ifp); 480 if (error) { 481 ifm->ifm_cur = oldentry; 482 ifm->ifm_media = oldmedia; 483 } 484 IFMEDIA_IOCTL_UNLOCK(ifm); 485 break; 486 } 487 488 /* Get list of available media and current media on interface. */ 489 case SIOCGIFMEDIA: 490 { 491 int nwords1, nwords2; 492 493 if (ifmr->ifm_count < 0) { 494 error = EINVAL; 495 break; 496 } 497 498 IFMEDIA_IOCTL_LOCK(ifm); 499 ifmr->ifm_active = ifmr->ifm_current = ifm->ifm_cur ? 500 ifm->ifm_cur->ifm_media : IFM_NONE; 501 ifmr->ifm_mask = ifm->ifm_mask; 502 ifmr->ifm_status = 0; 503 ifmedia_status(ifm, ifp, ifmr); 504 505 /* 506 * Count them so we know how much is the max we'll 507 * need. 508 */ 509 nwords1 = nwords2 = ifmedia_getwords(ifm, NULL, 0); 510 IFMEDIA_IOCTL_UNLOCK(ifm); 511 512 if (ifmr->ifm_count != 0) { 513 int maxwords = MIN(nwords1, ifmr->ifm_count); 514 int *kptr = kmem_zalloc(maxwords * sizeof(int), 515 KM_SLEEP); 516 517 ifmedia_lock(ifm); 518 nwords2 = ifmedia_getwords(ifm, kptr, maxwords); 519 ifmedia_unlock(ifm); 520 error = copyout(kptr, ifmr->ifm_ulist, 521 maxwords * sizeof(int)); 522 if (error == 0 && nwords2 > nwords1) 523 error = E2BIG; /* oops! */ 524 kmem_free(kptr, maxwords * sizeof(int)); 525 } 526 /* Update with the real number */ 527 ifmr->ifm_count = nwords2; 528 break; 529 } 530 531 default: 532 error = EINVAL; 533 break; 534 } 535 536 KERNEL_UNLOCK_UNLESS_IFP_MPSAFE(ifp); 537 538 return error; 539 } 540 541 /* 542 * Find media entry matching a given ifm word. 543 */ 544 static struct ifmedia_entry * 545 ifmedia_match_locked(struct ifmedia *ifm, u_int target, u_int mask) 546 { 547 struct ifmedia_entry *match, *next; 548 549 match = NULL; 550 mask = ~mask; 551 552 TAILQ_FOREACH(next, &ifm->ifm_list, ifm_list) { 553 if ((next->ifm_media & mask) == (target & mask)) { 554 if (match) { 555 #if defined(IFMEDIA_DEBUG) || defined(DIAGNOSTIC) 556 printf("ifmedia_match: multiple match for " 557 "0x%x/0x%x, selected instance %d\n", 558 target, mask, IFM_INST(match->ifm_media)); 559 #endif 560 break; 561 } 562 match = next; 563 } 564 } 565 566 return match; 567 } 568 569 struct ifmedia_entry * 570 ifmedia_match(struct ifmedia *ifm, u_int target, u_int mask) 571 { 572 struct ifmedia_entry *match; 573 574 /* 575 * N.B. We expect the caller is responsible fot the lifecycle 576 * of the media entries. Use with extreme caution. 577 */ 578 579 ifmedia_lock(ifm); 580 match = ifmedia_match_locked(ifm, target, mask); 581 ifmedia_unlock(ifm); 582 return match; 583 } 584 585 /* 586 * Delete all media for a given instance. 587 */ 588 void 589 ifmedia_delete_instance(struct ifmedia *ifm, u_int inst) 590 { 591 struct ifmedia_entry *ife, *nife; 592 TAILQ_HEAD(, ifmedia_entry) dead_entries; 593 594 TAILQ_INIT(&dead_entries); 595 596 ifmedia_lock(ifm); 597 TAILQ_FOREACH_SAFE(ife, &ifm->ifm_list, ifm_list, nife) { 598 if (inst == IFM_INST_ANY || 599 inst == IFM_INST(ife->ifm_media)) { 600 if (ifm->ifm_cur == ife) { 601 ifm->ifm_cur = NULL; 602 ifm->ifm_media = IFM_NONE; 603 } 604 TAILQ_REMOVE(&ifm->ifm_list, ife, ifm_list); 605 TAILQ_INSERT_TAIL(&dead_entries, ife, ifm_list); 606 } 607 } 608 ifmedia_unlock(ifm); 609 610 TAILQ_FOREACH_SAFE(ife, &dead_entries, ifm_list, nife) { 611 TAILQ_REMOVE(&dead_entries, ife, ifm_list); 612 kmem_free(ife, sizeof(*ife)); 613 } 614 } 615 616 void 617 ifmedia_removeall(struct ifmedia *ifm) 618 { 619 620 ifmedia_delete_instance(ifm, IFM_INST_ANY); 621 } 622 623 /* 624 * Compute the interface `baudrate' from the media, for the interface 625 * metrics (used by routing daemons). 626 */ 627 static const struct ifmedia_baudrate ifmedia_baudrate_descriptions[] = 628 IFM_BAUDRATE_DESCRIPTIONS; 629 630 uint64_t 631 ifmedia_baudrate(int mword) 632 { 633 int i; 634 635 for (i = 0; ifmedia_baudrate_descriptions[i].ifmb_word != 0; i++) { 636 if (IFM_TYPE_SUBTYPE_MATCH(mword, 637 ifmedia_baudrate_descriptions[i].ifmb_word)) 638 return ifmedia_baudrate_descriptions[i].ifmb_baudrate; 639 } 640 641 /* Not known. */ 642 return 0; 643 } 644 645 #ifdef IFMEDIA_DEBUG 646 647 static const struct ifmedia_description ifm_type_descriptions[] = 648 IFM_TYPE_DESCRIPTIONS; 649 650 static const struct ifmedia_description ifm_subtype_descriptions[] = 651 IFM_SUBTYPE_DESCRIPTIONS; 652 653 static const struct ifmedia_description ifm_option_descriptions[] = 654 IFM_OPTION_DESCRIPTIONS; 655 656 /* 657 * print a media word. 658 */ 659 static void 660 ifmedia_printword(int ifmw) 661 { 662 const struct ifmedia_description *desc; 663 int seen_option = 0; 664 665 /* Print the top-level interface type. */ 666 for (desc = ifm_type_descriptions; desc->ifmt_string != NULL; 667 desc++) { 668 if (IFM_TYPE(ifmw) == desc->ifmt_word) 669 break; 670 } 671 if (desc->ifmt_string == NULL) 672 printf("<unknown type> "); 673 else 674 printf("%s ", desc->ifmt_string); 675 676 /* Print the subtype. */ 677 for (desc = ifm_subtype_descriptions; desc->ifmt_string != NULL; 678 desc++) { 679 if (IFM_TYPE_MATCH(desc->ifmt_word, ifmw) && 680 IFM_SUBTYPE(desc->ifmt_word) == IFM_SUBTYPE(ifmw)) 681 break; 682 } 683 if (desc->ifmt_string == NULL) 684 printf("<unknown subtype>"); 685 else 686 printf("%s", desc->ifmt_string); 687 688 /* Print any options. */ 689 for (desc = ifm_option_descriptions; desc->ifmt_string != NULL; 690 desc++) { 691 if (IFM_TYPE_MATCH(desc->ifmt_word, ifmw) && 692 (ifmw & desc->ifmt_word) != 0 && 693 (seen_option & IFM_OPTIONS(desc->ifmt_word)) == 0) { 694 if (seen_option == 0) 695 printf(" <"); 696 printf("%s%s", seen_option ? "," : "", 697 desc->ifmt_string); 698 seen_option |= IFM_OPTIONS(desc->ifmt_word); 699 } 700 } 701 printf("%s\n", seen_option ? ">" : ""); 702 } 703 704 #endif /* IFMEDIA_DEBUG */ 705