1 /* $NetBSD: if_media.c,v 1.55 2026/07/26 20:57:28 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.55 2026/07/26 20:57:28 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 96 /* 97 * Compile-time options: 98 * IFMEDIA_DEBUG: 99 * Turn on implementation-level debug printfs. 100 * Useful for debugging newly-ported drivers. 101 */ 102 103 #ifdef IFMEDIA_DEBUG 104 int ifmedia_debug = 0; 105 static void ifmedia_printword(int); 106 #endif 107 108 /* 109 * We need to implement a recursive mutex to handle the un-converted 110 * driver case. For a fully MP-safe driver, the media lock will be 111 * held before calling any of the entry points that require it. However, 112 * this is not necessarily the case for a driver that hasn't yet been 113 * converted, and the entry point calls may be nested (for example 114 * mii_ifmedia_change -> ether_mediachange -> mii_mediachg). Luckily, 115 * the nesting won't be very deep, and 4 nested holds should be plenty. 116 */ 117 #define IFM_L_OWNLOCK 0x01 118 #define IFM_L_COUNT_MASK 0x3UL 119 #define IFM_L_CPU_MASK ~(IFM_L_COUNT_MASK) 120 121 void 122 ifmedia_lock_for_legacy(struct ifmedia *ifm) 123 { 124 uintptr_t cnt = IFM_L_OWNLOCK; 125 uintptr_t ci; 126 127 if (mutex_tryenter(ifm->ifm_lock)) { 128 goto gotit; 129 } 130 131 kpreempt_disable(); 132 ci = (uintptr_t)curcpu(); 133 if ((ifm->ifm_legacy & IFM_L_CPU_MASK) == ci) { 134 cnt = ifm->ifm_legacy & IFM_L_COUNT_MASK; 135 KASSERT(cnt < IFM_L_COUNT_MASK); 136 cnt++; 137 kpreempt_enable(); 138 goto gotit; 139 } 140 kpreempt_enable(); 141 142 mutex_enter(ifm->ifm_lock); 143 gotit: 144 KASSERT(kpreempt_disabled()); 145 ci = (uintptr_t)curcpu(); 146 KASSERT((ci & IFM_L_CPU_MASK) == ci); 147 ifm->ifm_legacy = ci | cnt; 148 } 149 150 void 151 ifmedia_unlock_for_legacy(struct ifmedia *ifm) 152 { 153 uintptr_t cnt; 154 uintptr_t ci = (uintptr_t)curcpu(); 155 156 KASSERT(kpreempt_disabled()); 157 KASSERT((ifm->ifm_legacy & IFM_L_CPU_MASK) == ci); 158 cnt = ifm->ifm_legacy & IFM_L_COUNT_MASK; 159 KASSERT(cnt != 0); 160 if (cnt == IFM_L_OWNLOCK) { 161 ifm->ifm_legacy = IFM_L_OWNLOCK; 162 mutex_exit(ifm->ifm_lock); 163 return; 164 } 165 cnt--; 166 ifm->ifm_legacy = ci | cnt; 167 } 168 169 /* 170 * Initialize if_media struct for a specific interface instance. 171 */ 172 void 173 ifmedia_init_with_lock(struct ifmedia *ifm, int dontcare_mask, 174 ifm_change_cb_t change_callback, ifm_stat_cb_t status_callback, 175 kmutex_t *lock) 176 { 177 178 /* 179 * XXX Would really like to assert: 180 * 181 * !if_is_mpsafe(ifp) || ((if_is_mpsafe(ifp) && lock != NULL) 182 * 183 * ...but we don't have access to the ifnet here. 184 */ 185 186 TAILQ_INIT(&ifm->ifm_list); 187 ifm->ifm_cur = NULL; 188 ifm->ifm_media = IFM_NONE; 189 ifm->ifm_mask = dontcare_mask; /* IF don't-care bits */ 190 ifm->ifm_change = change_callback; 191 ifm->ifm_status = status_callback; 192 ifm->ifm_legacy = 0; 193 194 if (lock == NULL) { 195 /* 196 * This is to support drivers that are not yet MP-safe 197 * with regard to the ifmedia layer. In these cases, 198 * we supply the lock and we ensure it's taken upon entry 199 * to various routines that expect it to be held. When 200 * we do this, we expect that the driver is in general a 201 * non-MP-safe driver and has already gone to splnet(). 202 */ 203 lock = mutex_obj_alloc(MUTEX_DEFAULT, IPL_NET); 204 ifm->ifm_legacy = IFM_L_OWNLOCK; 205 } 206 ifm->ifm_lock = lock; 207 } 208 209 void 210 ifmedia_init(struct ifmedia *ifm, int dontcare_mask, 211 ifm_change_cb_t change_callback, ifm_stat_cb_t status_callback) 212 { 213 ifmedia_init_with_lock(ifm, dontcare_mask, change_callback, 214 status_callback, NULL); 215 } 216 217 /* 218 * Free resources associated with an ifmedia. 219 */ 220 void 221 ifmedia_fini(struct ifmedia *ifm) 222 { 223 224 ifmedia_removeall(ifm); 225 226 if (ifm->ifm_legacy) { 227 KASSERT(ifm->ifm_legacy == IFM_L_OWNLOCK); 228 mutex_obj_free(ifm->ifm_lock); 229 } 230 ifm->ifm_legacy = 0; 231 ifm->ifm_lock = NULL; 232 } 233 234 int 235 ifmedia_change(struct ifmedia *ifm, struct ifnet *ifp) 236 { 237 int rv; 238 239 IFMEDIA_LOCK_FOR_LEGACY(ifm); 240 KASSERT(ifmedia_locked(ifm)); 241 if (ifm->ifm_change) 242 rv = (*ifm->ifm_change)(ifp); 243 else 244 rv = -1; 245 IFMEDIA_UNLOCK_FOR_LEGACY(ifm); 246 247 return rv; 248 } 249 250 static void 251 ifmedia_status(struct ifmedia *ifm, struct ifnet *ifp, struct ifmediareq *ifmr) 252 { 253 254 KASSERT(ifmedia_locked(ifm)); 255 if (ifm->ifm_status == NULL) 256 return; 257 (*ifm->ifm_status)(ifp, ifmr); 258 } 259 260 /* 261 * Add a media configuration to the list of supported media 262 * for a specific interface instance. 263 */ 264 static void 265 ifmedia_add_entry(struct ifmedia *ifm, int mword, int data, void *aux, 266 struct ifmedia_entry *entry) 267 { 268 269 #ifdef IFMEDIA_DEBUG 270 if (ifmedia_debug) { 271 if (ifm == NULL) { 272 printf("ifmedia_add: null ifm\n"); 273 return; 274 } 275 printf("Adding entry for "); 276 ifmedia_printword(mword); 277 } 278 #endif 279 280 entry->ifm_media = mword; 281 entry->ifm_data = data; 282 entry->ifm_aux = aux; 283 TAILQ_INSERT_TAIL(&ifm->ifm_list, entry, ifm_list); 284 } 285 286 void 287 ifmedia_add(struct ifmedia *ifm, int mword, int data, void *aux) 288 { 289 struct ifmedia_entry *entry; 290 291 entry = kmem_zalloc(sizeof(*entry), KM_SLEEP); 292 ifmedia_lock(ifm); 293 ifmedia_add_entry(ifm, mword, data, aux, entry); 294 ifmedia_unlock(ifm); 295 } 296 297 /* 298 * Add an array of media configurations to the list of 299 * supported media for a specific interface instance. 300 */ 301 void 302 ifmedia_list_add(struct ifmedia *ifm, struct ifmedia_entry *lp, int count) 303 { 304 int i; 305 306 for (i = 0; i < count; i++) 307 ifmedia_add(ifm, lp[i].ifm_media, lp[i].ifm_data, 308 lp[i].ifm_aux); 309 } 310 311 /* 312 * Set the default active media. 313 * 314 * Called by device-specific code which is assumed to have already 315 * selected the default media in hardware. We do _not_ call the 316 * media-change callback. 317 */ 318 void 319 ifmedia_set(struct ifmedia *ifm, int target) 320 { 321 struct ifmedia_entry *match, *entry = NULL; 322 323 ifmedia_lock(ifm); 324 match = ifmedia_match_locked(ifm, target, ifm->ifm_mask); 325 326 /* 327 * If we didn't find the requested media, then we try to fall 328 * back to target-type (IFM_ETHER, e.g.) | IFM_NONE. If that's 329 * not on the list, then we add it and set the media to it. 330 * 331 * Since ifmedia_set is almost always called with IFM_AUTO or 332 * with a known-good media, this really should only occur if we: 333 * 334 * a) didn't find any PHYs, or 335 * b) didn't find an autoselect option on the PHY when the 336 * parent ethernet driver expected to. 337 * 338 * In either case, it makes sense to select no media. 339 */ 340 if (match == NULL) { 341 printf("ifmedia_set: no match for 0x%x/0x%x\n", 342 target, ~ifm->ifm_mask); 343 target = (target & IFM_NMASK) | IFM_NONE; 344 match = ifmedia_match_locked(ifm, target, ifm->ifm_mask); 345 if (match == NULL) { 346 ifmedia_unlock(ifm); 347 entry = kmem_zalloc(sizeof(*entry), KM_SLEEP); 348 ifmedia_lock(ifm); 349 match = ifmedia_match_locked(ifm, target, 350 ifm->ifm_mask); 351 if (match == NULL) { 352 ifmedia_add_entry(ifm, target, 0, NULL, entry); 353 entry = NULL; 354 } 355 match = ifmedia_match_locked(ifm, target, 356 ifm->ifm_mask); 357 if (match == NULL) 358 panic("ifmedia_set failed"); 359 } 360 } 361 ifm->ifm_cur = match; 362 ifmedia_unlock(ifm); 363 364 if (entry) 365 kmem_free(entry, sizeof(*entry)); 366 367 #ifdef IFMEDIA_DEBUG 368 if (ifmedia_debug) { 369 printf("ifmedia_set: target "); 370 ifmedia_printword(target); 371 printf("ifmedia_set: setting to "); 372 ifmedia_printword(ifm->ifm_cur->ifm_media); 373 } 374 #endif 375 } 376 377 static int 378 ifmedia_getwords(struct ifmedia * const ifm, int *words, int maxwords) 379 { 380 struct ifmedia_entry *ep; 381 int nwords = 0; 382 383 KASSERT(ifmedia_locked(ifm)); 384 385 TAILQ_FOREACH(ep, &ifm->ifm_list, ifm_list) { 386 if (words != NULL && nwords < maxwords) { 387 words[nwords] = ep->ifm_media; 388 } 389 nwords++; 390 } 391 392 return nwords; 393 } 394 395 #define IFMEDIA_IOCTL_LOCK(ifm) \ 396 do { \ 397 if (ifmedia_islegacy(ifm)) \ 398 ifmedia_lock_for_legacy(ifm); \ 399 else \ 400 ifmedia_lock(ifm); \ 401 } while (/*CONSTCOND*/0) 402 403 #define IFMEDIA_IOCTL_UNLOCK(ifm) \ 404 do { \ 405 if (ifmedia_islegacy(ifm)) \ 406 ifmedia_unlock_for_legacy(ifm); \ 407 else \ 408 ifmedia_unlock(ifm); \ 409 } while (/*CONSTCOND*/0) 410 411 /* 412 * Device-independent media ioctl support function. 413 */ 414 int 415 ifmedia_ioctl(struct ifnet *ifp, struct ifreq *ifr, struct ifmedia *ifm, 416 u_long cmd) 417 { 418 struct ifmedia_entry *match; 419 struct ifmediareq *ifmr = (struct ifmediareq *)ifr; 420 int error = 0; 421 422 if (ifp == NULL || ifr == NULL || ifm == NULL) 423 return EINVAL; 424 425 KERNEL_LOCK_UNLESS_IFP_MPSAFE(ifp); 426 427 switch (cmd) { 428 case SIOCSIFMEDIA: /* Set the current media. */ 429 { 430 struct ifmedia_entry *oldentry; 431 u_int oldmedia; 432 u_int newmedia = ifr->ifr_media; 433 434 IFMEDIA_IOCTL_LOCK(ifm); 435 436 match = ifmedia_match_locked(ifm, newmedia, ifm->ifm_mask); 437 if (match == NULL) { 438 #ifdef IFMEDIA_DEBUG 439 if (ifmedia_debug) { 440 printf("ifmedia_ioctl: no media found for " 441 "0x%08x\n", newmedia); 442 } 443 #endif 444 IFMEDIA_IOCTL_UNLOCK(ifm); 445 error = EINVAL; 446 break; 447 } 448 449 /* 450 * If no change, we're done. 451 * XXX Automedia may involve software intervention. 452 * Keep going in case the connected media changed. 453 * Similarly, if best match changed (kernel debugger?). 454 */ 455 if ((IFM_SUBTYPE(newmedia) != IFM_AUTO) && 456 (newmedia == ifm->ifm_media) && (match == ifm->ifm_cur)) { 457 IFMEDIA_IOCTL_UNLOCK(ifm); 458 break; 459 } 460 461 /* 462 * We found a match, now make the driver switch to it. 463 * Make sure to preserve our old media type in case the 464 * driver can't switch. 465 */ 466 #ifdef IFMEDIA_DEBUG 467 if (ifmedia_debug) { 468 printf("ifmedia_ioctl: switching %s to ", 469 ifp->if_xname); 470 ifmedia_printword(match->ifm_media); 471 } 472 #endif 473 oldentry = ifm->ifm_cur; 474 oldmedia = ifm->ifm_media; 475 ifm->ifm_cur = match; 476 ifm->ifm_media = newmedia; 477 error = ifmedia_change(ifm, ifp); 478 if (error) { 479 ifm->ifm_cur = oldentry; 480 ifm->ifm_media = oldmedia; 481 } 482 IFMEDIA_IOCTL_UNLOCK(ifm); 483 break; 484 } 485 486 /* Get list of available media and current media on interface. */ 487 case SIOCGIFMEDIA: 488 { 489 int nwords1, nwords2; 490 491 if (ifmr->ifm_count < 0) { 492 error = EINVAL; 493 break; 494 } 495 496 IFMEDIA_IOCTL_LOCK(ifm); 497 ifmr->ifm_active = ifmr->ifm_current = ifm->ifm_cur ? 498 ifm->ifm_cur->ifm_media : IFM_NONE; 499 ifmr->ifm_mask = ifm->ifm_mask; 500 ifmr->ifm_status = 0; 501 ifmedia_status(ifm, ifp, ifmr); 502 503 /* 504 * Count them so we know how much is the max we'll 505 * need. 506 */ 507 nwords1 = nwords2 = ifmedia_getwords(ifm, NULL, 0); 508 IFMEDIA_IOCTL_UNLOCK(ifm); 509 510 if (ifmr->ifm_count != 0) { 511 int maxwords = MIN(nwords1, ifmr->ifm_count); 512 int *kptr = kmem_zalloc(maxwords * sizeof(int), 513 KM_SLEEP); 514 515 ifmedia_lock(ifm); 516 nwords2 = ifmedia_getwords(ifm, kptr, maxwords); 517 ifmedia_unlock(ifm); 518 error = copyout(kptr, ifmr->ifm_ulist, 519 maxwords * sizeof(int)); 520 if (error == 0 && nwords2 > nwords1) 521 error = E2BIG; /* oops! */ 522 kmem_free(kptr, maxwords * sizeof(int)); 523 } 524 /* Update with the real number */ 525 ifmr->ifm_count = nwords2; 526 break; 527 } 528 529 default: 530 error = EINVAL; 531 break; 532 } 533 534 KERNEL_UNLOCK_UNLESS_IFP_MPSAFE(ifp); 535 536 return error; 537 } 538 539 /* 540 * Find media entry matching a given ifm word. 541 */ 542 struct ifmedia_entry * 543 ifmedia_match_locked(struct ifmedia *ifm, u_int target, u_int mask) 544 { 545 struct ifmedia_entry *match, *next; 546 547 match = NULL; 548 mask = ~mask; 549 550 TAILQ_FOREACH(next, &ifm->ifm_list, ifm_list) { 551 if ((next->ifm_media & mask) == (target & mask)) { 552 if (match) { 553 #if defined(IFMEDIA_DEBUG) || defined(DIAGNOSTIC) 554 printf("ifmedia_match: multiple match for " 555 "0x%x/0x%x, selected instance %d\n", 556 target, mask, IFM_INST(match->ifm_media)); 557 #endif 558 break; 559 } 560 match = next; 561 } 562 } 563 564 return match; 565 } 566 567 struct ifmedia_entry * 568 ifmedia_match(struct ifmedia *ifm, u_int target, u_int mask) 569 { 570 struct ifmedia_entry *match; 571 572 /* 573 * N.B. We expect the caller is responsible fot the lifecycle 574 * of the media entries. Use with extreme caution. 575 */ 576 577 ifmedia_lock(ifm); 578 match = ifmedia_match_locked(ifm, target, mask); 579 ifmedia_unlock(ifm); 580 return match; 581 } 582 583 /* 584 * Delete all media for a given instance. 585 */ 586 void 587 ifmedia_delete_instance(struct ifmedia *ifm, u_int inst) 588 { 589 struct ifmedia_entry *ife, *nife; 590 TAILQ_HEAD(, ifmedia_entry) dead_entries; 591 592 TAILQ_INIT(&dead_entries); 593 594 ifmedia_lock(ifm); 595 TAILQ_FOREACH_SAFE(ife, &ifm->ifm_list, ifm_list, nife) { 596 if (inst == IFM_INST_ANY || 597 inst == IFM_INST(ife->ifm_media)) { 598 if (ifm->ifm_cur == ife) { 599 ifm->ifm_cur = NULL; 600 ifm->ifm_media = IFM_NONE; 601 } 602 TAILQ_REMOVE(&ifm->ifm_list, ife, ifm_list); 603 TAILQ_INSERT_TAIL(&dead_entries, ife, ifm_list); 604 } 605 } 606 ifmedia_unlock(ifm); 607 608 TAILQ_FOREACH_SAFE(ife, &dead_entries, ifm_list, nife) { 609 TAILQ_REMOVE(&dead_entries, ife, ifm_list); 610 kmem_free(ife, sizeof(*ife)); 611 } 612 } 613 614 void 615 ifmedia_removeall(struct ifmedia *ifm) 616 { 617 618 ifmedia_delete_instance(ifm, IFM_INST_ANY); 619 } 620 621 /* 622 * Compute the interface `baudrate' from the media, for the interface 623 * metrics (used by routing daemons). 624 */ 625 static const struct ifmedia_baudrate ifmedia_baudrate_descriptions[] = 626 IFM_BAUDRATE_DESCRIPTIONS; 627 628 uint64_t 629 ifmedia_baudrate(int mword) 630 { 631 int i; 632 633 for (i = 0; ifmedia_baudrate_descriptions[i].ifmb_word != 0; i++) { 634 if (IFM_TYPE_SUBTYPE_MATCH(mword, 635 ifmedia_baudrate_descriptions[i].ifmb_word)) 636 return ifmedia_baudrate_descriptions[i].ifmb_baudrate; 637 } 638 639 /* Not known. */ 640 return 0; 641 } 642 643 #ifdef IFMEDIA_DEBUG 644 645 static const struct ifmedia_description ifm_type_descriptions[] = 646 IFM_TYPE_DESCRIPTIONS; 647 648 static const struct ifmedia_description ifm_subtype_descriptions[] = 649 IFM_SUBTYPE_DESCRIPTIONS; 650 651 static const struct ifmedia_description ifm_option_descriptions[] = 652 IFM_OPTION_DESCRIPTIONS; 653 654 /* 655 * print a media word. 656 */ 657 static void 658 ifmedia_printword(int ifmw) 659 { 660 const struct ifmedia_description *desc; 661 int seen_option = 0; 662 663 /* Print the top-level interface type. */ 664 for (desc = ifm_type_descriptions; desc->ifmt_string != NULL; 665 desc++) { 666 if (IFM_TYPE(ifmw) == desc->ifmt_word) 667 break; 668 } 669 if (desc->ifmt_string == NULL) 670 printf("<unknown type> "); 671 else 672 printf("%s ", desc->ifmt_string); 673 674 /* Print the subtype. */ 675 for (desc = ifm_subtype_descriptions; desc->ifmt_string != NULL; 676 desc++) { 677 if (IFM_TYPE_MATCH(desc->ifmt_word, ifmw) && 678 IFM_SUBTYPE(desc->ifmt_word) == IFM_SUBTYPE(ifmw)) 679 break; 680 } 681 if (desc->ifmt_string == NULL) 682 printf("<unknown subtype>"); 683 else 684 printf("%s", desc->ifmt_string); 685 686 /* Print any options. */ 687 for (desc = ifm_option_descriptions; desc->ifmt_string != NULL; 688 desc++) { 689 if (IFM_TYPE_MATCH(desc->ifmt_word, ifmw) && 690 (ifmw & desc->ifmt_word) != 0 && 691 (seen_option & IFM_OPTIONS(desc->ifmt_word)) == 0) { 692 if (seen_option == 0) 693 printf(" <"); 694 printf("%s%s", seen_option ? "," : "", 695 desc->ifmt_string); 696 seen_option |= IFM_OPTIONS(desc->ifmt_word); 697 } 698 } 699 printf("%s\n", seen_option ? ">" : ""); 700 } 701 702 #endif /* IFMEDIA_DEBUG */ 703