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