1 /* $NetBSD: if_media.c,v 1.1 1997/03/17 02:55:15 thorpej Exp $ */ 2 3 /*- 4 * SPDX-License-Identifier: BSD-4-Clause 5 * 6 * Copyright (c) 1997 7 * Jonathan Stone and Jason R. Thorpe. All rights reserved. 8 * 9 * This software is derived from information provided by Matt Thomas. 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 * 3. All advertising materials mentioning features or use of this software 20 * must display the following acknowledgement: 21 * This product includes software developed by Jonathan Stone 22 * and Jason R. Thorpe for the NetBSD Project. 23 * 4. The names of the authors may not be used to endorse or promote products 24 * derived from this software without specific prior written permission. 25 * 26 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR 27 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 28 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 29 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 30 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 31 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 32 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 33 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 34 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 35 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 36 * SUCH DAMAGE. 37 */ 38 39 /* 40 * BSD/OS-compatible network interface media selection. 41 * 42 * Where it is safe to do so, this code strays slightly from the BSD/OS 43 * design. Software which uses the API (device drivers, basically) 44 * shouldn't notice any difference. 45 * 46 * Many thanks to Matt Thomas for providing the information necessary 47 * to implement this interface. 48 */ 49 50 #include <sys/cdefs.h> 51 #include "opt_ifmedia.h" 52 53 #include <sys/param.h> 54 #include <sys/systm.h> 55 #include <sys/socket.h> 56 #include <sys/sockio.h> 57 #include <sys/malloc.h> 58 #include <sys/module.h> 59 #include <sys/sysctl.h> 60 61 #include <net/if.h> 62 #include <net/if_media.h> 63 64 /* 65 * Compile-time options: 66 * IFMEDIA_DEBUG: 67 * turn on implementation-level debug printfs. 68 * Useful for debugging newly-ported drivers. 69 */ 70 71 static struct ifmedia_entry *ifmedia_match(struct ifmedia *ifm, 72 int flags, int mask); 73 74 #ifdef IFMEDIA_DEBUG 75 #include <net/if_var.h> 76 #include <net/if_private.h> 77 int ifmedia_debug = 0; 78 SYSCTL_INT(_debug, OID_AUTO, ifmedia, CTLFLAG_RW, &ifmedia_debug, 79 0, "if_media debugging msgs"); 80 static void ifmedia_printword(int); 81 #endif 82 83 /* 84 * Initialize if_media struct for a specific interface instance. 85 */ 86 void 87 ifmedia_init(struct ifmedia *ifm, int dontcare_mask, 88 ifm_change_cb_t change_callback, ifm_stat_cb_t status_callback) 89 { 90 91 LIST_INIT(&ifm->ifm_list); 92 ifm->ifm_cur = NULL; 93 ifm->ifm_media = 0; 94 ifm->ifm_mask = dontcare_mask; /* IF don't-care bits */ 95 ifm->ifm_change = change_callback; 96 ifm->ifm_status = status_callback; 97 } 98 99 void 100 ifmedia_removeall(struct ifmedia *ifm) 101 { 102 struct ifmedia_entry *entry; 103 104 while ((entry = LIST_FIRST(&ifm->ifm_list)) != NULL) { 105 LIST_REMOVE(entry, ifm_list); 106 free(entry, M_IFADDR); 107 } 108 ifm->ifm_cur = NULL; 109 } 110 111 /* 112 * Add a media configuration to the list of supported media 113 * for a specific interface instance. 114 */ 115 void 116 ifmedia_add(struct ifmedia *ifm, int mword, int data, void *aux) 117 { 118 struct ifmedia_entry *entry; 119 120 #ifdef IFMEDIA_DEBUG 121 if (ifmedia_debug) { 122 if (ifm == NULL) { 123 printf("ifmedia_add: null ifm\n"); 124 return; 125 } 126 printf("Adding entry for (%#010x) ", mword); 127 ifmedia_printword(mword); 128 } 129 #endif 130 131 entry = malloc(sizeof(*entry), M_IFADDR, M_NOWAIT); 132 if (entry == NULL) 133 panic("ifmedia_add: can't malloc entry"); 134 135 entry->ifm_media = mword; 136 entry->ifm_data = data; 137 entry->ifm_aux = aux; 138 139 LIST_INSERT_HEAD(&ifm->ifm_list, entry, ifm_list); 140 } 141 142 /* 143 * Add an array of media configurations to the list of 144 * supported media for a specific interface instance. 145 */ 146 void 147 ifmedia_list_add(struct ifmedia *ifm, struct ifmedia_entry *lp, int count) 148 { 149 int i; 150 151 for (i = 0; i < count; i++) 152 ifmedia_add(ifm, lp[i].ifm_media, lp[i].ifm_data, 153 lp[i].ifm_aux); 154 } 155 156 /* 157 * Set the default active media. 158 * 159 * Called by device-specific code which is assumed to have already 160 * selected the default media in hardware. We do _not_ call the 161 * media-change callback. 162 */ 163 void 164 ifmedia_set(struct ifmedia *ifm, int target) 165 { 166 struct ifmedia_entry *match; 167 168 match = ifmedia_match(ifm, target, ifm->ifm_mask); 169 170 if (match == NULL) { 171 printf("ifmedia_set: no match for 0x%x/0x%x\n", 172 target, ~ifm->ifm_mask); 173 panic("ifmedia_set"); 174 } 175 ifm->ifm_cur = match; 176 177 #ifdef IFMEDIA_DEBUG 178 if (ifmedia_debug) { 179 printf("ifmedia_set: target "); 180 ifmedia_printword(target); 181 printf("ifmedia_set: setting to "); 182 ifmedia_printword(ifm->ifm_cur->ifm_media); 183 } 184 #endif 185 } 186 187 /* 188 * Given a media word, return one suitable for an application 189 * using the original encoding. 190 */ 191 static int 192 compat_media(int media) 193 { 194 195 if (IFM_TYPE(media) == IFM_ETHER && IFM_SUBTYPE(media) > IFM_OTHER) { 196 media &= ~(IFM_ETH_XTYPE|IFM_TMASK); 197 media |= IFM_OTHER; 198 } 199 return (media); 200 } 201 202 /* 203 * Device-independent media ioctl support function. 204 */ 205 int 206 ifmedia_ioctl(struct ifnet *ifp, struct ifreq *ifr, struct ifmedia *ifm, 207 u_long cmd) 208 { 209 struct ifmedia_entry *match; 210 struct ifmediareq *ifmr = (struct ifmediareq *) ifr; 211 int error = 0; 212 213 if (ifp == NULL || ifr == NULL || ifm == NULL) 214 return (EINVAL); 215 216 switch (cmd) { 217 /* 218 * Set the current media. 219 */ 220 case SIOCSIFMEDIA: 221 { 222 struct ifmedia_entry *oldentry; 223 int oldmedia; 224 int newmedia = ifr->ifr_media; 225 226 match = ifmedia_match(ifm, newmedia, ifm->ifm_mask); 227 if (match == NULL) { 228 #ifdef IFMEDIA_DEBUG 229 if (ifmedia_debug) { 230 printf( 231 "ifmedia_ioctl: no media found for %#010x mask %#010x\n", 232 newmedia, ifm->ifm_mask); 233 } 234 #endif 235 return (ENXIO); 236 } 237 238 /* 239 * If no change, we're done. 240 * XXX Automedia may invole software intervention. 241 * Keep going in case the connected media changed. 242 * Similarly, if best match changed (kernel debugger?). 243 */ 244 if (IFM_SUBTYPE(newmedia) != IFM_AUTO && 245 newmedia == ifm->ifm_media && match == ifm->ifm_cur) 246 return (0); 247 248 /* 249 * We found a match, now make the driver switch to it. 250 * Make sure to preserve our old media type in case the 251 * driver can't switch. 252 */ 253 #ifdef IFMEDIA_DEBUG 254 if (ifmedia_debug) { 255 printf("ifmedia_ioctl: switching %s to ", 256 ifp->if_xname); 257 ifmedia_printword(match->ifm_media); 258 } 259 #endif 260 oldentry = ifm->ifm_cur; 261 oldmedia = ifm->ifm_media; 262 ifm->ifm_cur = match; 263 ifm->ifm_media = newmedia; 264 error = (*ifm->ifm_change)(ifp); 265 if (error) { 266 ifm->ifm_cur = oldentry; 267 ifm->ifm_media = oldmedia; 268 } 269 break; 270 } 271 272 /* 273 * Get list of available media and current media on interface. 274 */ 275 case SIOCGIFMEDIA: 276 case SIOCGIFXMEDIA: 277 { 278 struct ifmedia_entry *ep; 279 int i; 280 281 if (ifmr->ifm_count < 0) 282 return (EINVAL); 283 284 if (cmd == SIOCGIFMEDIA) { 285 ifmr->ifm_active = ifmr->ifm_current = ifm->ifm_cur ? 286 compat_media(ifm->ifm_cur->ifm_media) : IFM_NONE; 287 } else { 288 ifmr->ifm_active = ifmr->ifm_current = ifm->ifm_cur ? 289 ifm->ifm_cur->ifm_media : IFM_NONE; 290 } 291 ifmr->ifm_mask = ifm->ifm_mask; 292 ifmr->ifm_status = 0; 293 (*ifm->ifm_status)(ifp, ifmr); 294 295 /* 296 * If there are more interfaces on the list, count 297 * them. This allows the caller to set ifmr->ifm_count 298 * to 0 on the first call to know how much space to 299 * allocate. 300 */ 301 i = 0; 302 LIST_FOREACH(ep, &ifm->ifm_list, ifm_list) { 303 if (i < ifmr->ifm_count) { 304 error = copyout(&ep->ifm_media, 305 ifmr->ifm_ulist + i, sizeof(int)); 306 if (error != 0) 307 break; 308 } 309 i++; 310 } 311 if (error == 0 && i > ifmr->ifm_count) 312 error = ifmr->ifm_count != 0 ? E2BIG : 0; 313 ifmr->ifm_count = i; 314 break; 315 } 316 317 default: 318 return (EINVAL); 319 } 320 321 return (error); 322 } 323 324 /* 325 * Find media entry matching a given ifm word. 326 * 327 */ 328 static struct ifmedia_entry * 329 ifmedia_match(struct ifmedia *ifm, int target, int mask) 330 { 331 struct ifmedia_entry *match, *next; 332 333 match = NULL; 334 mask = ~mask; 335 336 LIST_FOREACH(next, &ifm->ifm_list, ifm_list) { 337 if ((next->ifm_media & mask) == (target & mask)) { 338 #if defined(IFMEDIA_DEBUG) || defined(DIAGNOSTIC) 339 if (match) { 340 printf("ifmedia_match: multiple match for " 341 "%#010x/%#010x\n", target, mask); 342 } 343 #endif 344 match = next; 345 } 346 } 347 348 return (match); 349 } 350 351 /* 352 * Compute the interface `baudrate' from the media, for the interface 353 * metrics (used by routing daemons). 354 */ 355 static const struct ifmedia_baudrate ifmedia_baudrate_descriptions[] = 356 IFM_BAUDRATE_DESCRIPTIONS; 357 358 uint64_t 359 ifmedia_baudrate(int mword) 360 { 361 int i; 362 363 for (i = 0; ifmedia_baudrate_descriptions[i].ifmb_word != 0; i++) { 364 if (IFM_TYPE_MATCH(mword, ifmedia_baudrate_descriptions[i]. 365 ifmb_word)) 366 return (ifmedia_baudrate_descriptions[i].ifmb_baudrate); 367 } 368 369 /* Not known. */ 370 return (0); 371 } 372 373 #ifdef IFMEDIA_DEBUG 374 static const struct ifmedia_description ifm_type_descriptions[] = 375 IFM_TYPE_DESCRIPTIONS; 376 377 static const struct ifmedia_description ifm_subtype_ethernet_descriptions[] = 378 IFM_SUBTYPE_ETHERNET_DESCRIPTIONS; 379 380 static const struct ifmedia_description 381 ifm_subtype_ethernet_option_descriptions[] = 382 IFM_SUBTYPE_ETHERNET_OPTION_DESCRIPTIONS; 383 384 static const struct ifmedia_description ifm_subtype_ieee80211_descriptions[] = 385 IFM_SUBTYPE_IEEE80211_DESCRIPTIONS; 386 387 static const struct ifmedia_description 388 ifm_subtype_ieee80211_option_descriptions[] = 389 IFM_SUBTYPE_IEEE80211_OPTION_DESCRIPTIONS; 390 391 static const struct ifmedia_description 392 ifm_subtype_ieee80211_mode_descriptions[] = 393 IFM_SUBTYPE_IEEE80211_MODE_DESCRIPTIONS; 394 395 static const struct ifmedia_description ifm_subtype_atm_descriptions[] = 396 IFM_SUBTYPE_ATM_DESCRIPTIONS; 397 398 static const struct ifmedia_description ifm_subtype_atm_option_descriptions[] = 399 IFM_SUBTYPE_ATM_OPTION_DESCRIPTIONS; 400 401 static const struct ifmedia_description ifm_subtype_shared_descriptions[] = 402 IFM_SUBTYPE_SHARED_DESCRIPTIONS; 403 404 static const struct ifmedia_description ifm_shared_option_descriptions[] = 405 IFM_SHARED_OPTION_DESCRIPTIONS; 406 407 struct ifmedia_type_to_subtype { 408 const struct ifmedia_description *subtypes; 409 const struct ifmedia_description *options; 410 const struct ifmedia_description *modes; 411 }; 412 413 /* must be in the same order as IFM_TYPE_DESCRIPTIONS */ 414 static const struct ifmedia_type_to_subtype ifmedia_types_to_subtypes[] = { 415 { 416 &ifm_subtype_ethernet_descriptions[0], 417 &ifm_subtype_ethernet_option_descriptions[0], 418 NULL, 419 }, 420 { 421 &ifm_subtype_ieee80211_descriptions[0], 422 &ifm_subtype_ieee80211_option_descriptions[0], 423 &ifm_subtype_ieee80211_mode_descriptions[0] 424 }, 425 { 426 &ifm_subtype_atm_descriptions[0], 427 &ifm_subtype_atm_option_descriptions[0], 428 NULL, 429 }, 430 }; 431 432 /* 433 * print a media word. 434 */ 435 static void 436 ifmedia_printword(int ifmw) 437 { 438 const struct ifmedia_description *desc; 439 const struct ifmedia_type_to_subtype *ttos; 440 int seen_option = 0; 441 442 /* Find the top-level interface type. */ 443 for (desc = ifm_type_descriptions, ttos = ifmedia_types_to_subtypes; 444 desc->ifmt_string != NULL; desc++, ttos++) 445 if (IFM_TYPE(ifmw) == desc->ifmt_word) 446 break; 447 if (desc->ifmt_string == NULL) { 448 printf("<unknown type>\n"); 449 return; 450 } 451 printf("%s", desc->ifmt_string); 452 453 /* Any mode. */ 454 for (desc = ttos->modes; desc && desc->ifmt_string != NULL; desc++) 455 if (IFM_MODE(ifmw) == desc->ifmt_word) { 456 if (desc->ifmt_string != NULL) 457 printf(" mode %s", desc->ifmt_string); 458 break; 459 } 460 461 /* 462 * Check for the shared subtype descriptions first, then the 463 * type-specific ones. 464 */ 465 for (desc = ifm_subtype_shared_descriptions; 466 desc->ifmt_string != NULL; desc++) 467 if (IFM_SUBTYPE(ifmw) == desc->ifmt_word) 468 goto got_subtype; 469 470 for (desc = ttos->subtypes; desc->ifmt_string != NULL; desc++) 471 if (IFM_SUBTYPE(ifmw) == desc->ifmt_word) 472 break; 473 if (desc->ifmt_string == NULL) { 474 printf(" <unknown subtype>\n"); 475 return; 476 } 477 478 got_subtype: 479 printf(" %s", desc->ifmt_string); 480 481 /* 482 * Look for shared options. 483 */ 484 for (desc = ifm_shared_option_descriptions; 485 desc->ifmt_string != NULL; desc++) { 486 if (ifmw & desc->ifmt_word) { 487 if (seen_option == 0) 488 printf(" <"); 489 printf("%s%s", seen_option++ ? "," : "", 490 desc->ifmt_string); 491 } 492 } 493 494 /* 495 * Look for subtype-specific options. 496 */ 497 for (desc = ttos->options; desc->ifmt_string != NULL; desc++) { 498 if (ifmw & desc->ifmt_word) { 499 if (seen_option == 0) 500 printf(" <"); 501 printf("%s%s", seen_option++ ? "," : "", 502 desc->ifmt_string); 503 } 504 } 505 printf("%s\n", seen_option ? ">" : ""); 506 } 507 #endif /* IFMEDIA_DEBUG */ 508