1 /* $NetBSD: if_media.c,v 1.1 1997/03/17 02:55:15 thorpej Exp $ */ 2 /* $FreeBSD: src/sys/net/if_media.c,v 1.9.2.4 2001/07/04 00:12:38 brooks Exp $ */ 3 /* $DragonFly: src/sys/net/if_media.c,v 1.6 2004/04/22 04:21:29 dillon Exp $ */ 4 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/param.h> 51 #include <sys/systm.h> 52 #include <sys/socket.h> 53 #include <sys/sockio.h> 54 #include <sys/malloc.h> 55 56 #include <net/if.h> 57 #include <net/if_media.h> 58 59 /* 60 * Compile-time options: 61 * IFMEDIA_DEBUG: 62 * turn on implementation-level debug printfs. 63 * Useful for debugging newly-ported drivers. 64 */ 65 66 static struct ifmedia_entry *ifmedia_match (struct ifmedia *ifm, 67 int flags, int mask); 68 69 #ifdef IFMEDIA_DEBUG 70 int ifmedia_debug = 0; 71 static void ifmedia_printword (int); 72 #endif 73 74 /* 75 * Initialize if_media struct for a specific interface instance. 76 */ 77 void 78 ifmedia_init(ifm, dontcare_mask, change_callback, status_callback) 79 struct ifmedia *ifm; 80 int dontcare_mask; 81 ifm_change_cb_t change_callback; 82 ifm_stat_cb_t status_callback; 83 { 84 85 LIST_INIT(&ifm->ifm_list); 86 ifm->ifm_cur = NULL; 87 ifm->ifm_media = 0; 88 ifm->ifm_mask = dontcare_mask; /* IF don't-care bits */ 89 ifm->ifm_change = change_callback; 90 ifm->ifm_status = status_callback; 91 } 92 93 void 94 ifmedia_removeall(ifm) 95 struct ifmedia *ifm; 96 { 97 struct ifmedia_entry *entry; 98 99 for (entry = LIST_FIRST(&ifm->ifm_list); entry; 100 entry = LIST_FIRST(&ifm->ifm_list)) { 101 LIST_REMOVE(entry, ifm_list); 102 free(entry, M_IFADDR); 103 } 104 } 105 106 /* 107 * Add a media configuration to the list of supported media 108 * for a specific interface instance. 109 */ 110 void 111 ifmedia_add(ifm, mword, data, aux) 112 struct ifmedia *ifm; 113 int mword; 114 int data; 115 void *aux; 116 { 117 struct ifmedia_entry *entry; 118 119 #ifdef IFMEDIA_DEBUG 120 if (ifmedia_debug) { 121 if (ifm == NULL) { 122 printf("ifmedia_add: null ifm\n"); 123 return; 124 } 125 printf("Adding entry for "); 126 ifmedia_printword(mword); 127 } 128 #endif 129 130 entry = malloc(sizeof(*entry), M_IFADDR, M_INTWAIT); 131 entry->ifm_media = mword; 132 entry->ifm_data = data; 133 entry->ifm_aux = aux; 134 135 LIST_INSERT_HEAD(&ifm->ifm_list, entry, ifm_list); 136 } 137 138 /* 139 * Add an array of media configurations to the list of 140 * supported media for a specific interface instance. 141 */ 142 void 143 ifmedia_list_add(ifm, lp, count) 144 struct ifmedia *ifm; 145 struct ifmedia_entry *lp; 146 int count; 147 { 148 int i; 149 150 for (i = 0; i < count; i++) 151 ifmedia_add(ifm, lp[i].ifm_media, lp[i].ifm_data, 152 lp[i].ifm_aux); 153 } 154 155 /* 156 * Set the default active media. 157 * 158 * Called by device-specific code which is assumed to have already 159 * selected the default media in hardware. We do _not_ call the 160 * media-change callback. 161 */ 162 void 163 ifmedia_set(ifm, target) 164 struct ifmedia *ifm; 165 int target; 166 167 { 168 struct ifmedia_entry *match; 169 170 match = ifmedia_match(ifm, target, ifm->ifm_mask); 171 172 if (match == NULL) { 173 printf("ifmedia_set: no match for 0x%x/0x%x\n", 174 target, ~ifm->ifm_mask); 175 panic("ifmedia_set"); 176 } 177 ifm->ifm_cur = match; 178 179 #ifdef IFMEDIA_DEBUG 180 if (ifmedia_debug) { 181 printf("ifmedia_set: target "); 182 ifmedia_printword(target); 183 printf("ifmedia_set: setting to "); 184 ifmedia_printword(ifm->ifm_cur->ifm_media); 185 } 186 #endif 187 } 188 189 /* 190 * Device-independent media ioctl support function. 191 */ 192 int 193 ifmedia_ioctl(ifp, ifr, ifm, cmd) 194 struct ifnet *ifp; 195 struct ifreq *ifr; 196 struct ifmedia *ifm; 197 u_long cmd; 198 { 199 struct ifmedia_entry *match; 200 struct ifmediareq *ifmr = (struct ifmediareq *) ifr; 201 int error = 0, sticky; 202 203 if (ifp == NULL || ifr == NULL || ifm == NULL) 204 return(EINVAL); 205 206 switch (cmd) { 207 208 /* 209 * Set the current media. 210 */ 211 case SIOCSIFMEDIA: 212 { 213 struct ifmedia_entry *oldentry; 214 int oldmedia; 215 int newmedia = ifr->ifr_media; 216 217 match = ifmedia_match(ifm, newmedia, ifm->ifm_mask); 218 if (match == NULL) { 219 #ifdef IFMEDIA_DEBUG 220 if (ifmedia_debug) { 221 printf( 222 "ifmedia_ioctl: no media found for 0x%x\n", 223 newmedia); 224 } 225 #endif 226 return (ENXIO); 227 } 228 229 /* 230 * If no change, we're done. 231 * XXX Automedia may invole software intervention. 232 * Keep going in case the the connected media changed. 233 * Similarly, if best match changed (kernel debugger?). 234 */ 235 if ((IFM_SUBTYPE(newmedia) != IFM_AUTO) && 236 (newmedia == ifm->ifm_media) && 237 (match == ifm->ifm_cur)) 238 return 0; 239 240 /* 241 * We found a match, now make the driver switch to it. 242 * Make sure to preserve our old media type in case the 243 * driver can't switch. 244 */ 245 #ifdef IFMEDIA_DEBUG 246 if (ifmedia_debug) { 247 printf("ifmedia_ioctl: switching %s to ", 248 ifp->if_xname); 249 ifmedia_printword(match->ifm_media); 250 } 251 #endif 252 oldentry = ifm->ifm_cur; 253 oldmedia = ifm->ifm_media; 254 ifm->ifm_cur = match; 255 ifm->ifm_media = newmedia; 256 error = (*ifm->ifm_change)(ifp); 257 if (error) { 258 ifm->ifm_cur = oldentry; 259 ifm->ifm_media = oldmedia; 260 } 261 break; 262 } 263 264 /* 265 * Get list of available media and current media on interface. 266 */ 267 case SIOCGIFMEDIA: 268 { 269 struct ifmedia_entry *ep; 270 int *kptr, count; 271 int usermax; /* user requested max */ 272 273 kptr = NULL; /* XXX gcc */ 274 275 ifmr->ifm_active = ifmr->ifm_current = ifm->ifm_cur ? 276 ifm->ifm_cur->ifm_media : IFM_NONE; 277 ifmr->ifm_mask = ifm->ifm_mask; 278 ifmr->ifm_status = 0; 279 (*ifm->ifm_status)(ifp, ifmr); 280 281 count = 0; 282 usermax = 0; 283 284 /* 285 * If there are more interfaces on the list, count 286 * them. This allows the caller to set ifmr->ifm_count 287 * to 0 on the first call to know how much space to 288 * allocate. 289 */ 290 LIST_FOREACH(ep, &ifm->ifm_list, ifm_list) 291 usermax++; 292 293 /* 294 * Don't allow the user to ask for too many 295 * or a negative number. 296 */ 297 if (ifmr->ifm_count > usermax) 298 ifmr->ifm_count = usermax; 299 else if (ifmr->ifm_count < 0) 300 return (EINVAL); 301 302 if (ifmr->ifm_count != 0) { 303 kptr = (int *)malloc(ifmr->ifm_count * sizeof(int), 304 M_TEMP, M_WAITOK); 305 306 /* 307 * Get the media words from the interface's list. 308 */ 309 ep = LIST_FIRST(&ifm->ifm_list); 310 for (; ep != NULL && count < ifmr->ifm_count; 311 ep = LIST_NEXT(ep, ifm_list), count++) 312 kptr[count] = ep->ifm_media; 313 314 if (ep != NULL) 315 error = E2BIG; /* oops! */ 316 } else { 317 count = usermax; 318 } 319 320 /* 321 * We do the copyout on E2BIG, because that's 322 * just our way of telling userland that there 323 * are more. This is the behavior I've observed 324 * under BSD/OS 3.0 325 */ 326 sticky = error; 327 if ((error == 0 || error == E2BIG) && ifmr->ifm_count != 0) { 328 error = copyout((caddr_t)kptr, 329 (caddr_t)ifmr->ifm_ulist, 330 ifmr->ifm_count * sizeof(int)); 331 } 332 333 if (error == 0) 334 error = sticky; 335 336 if (ifmr->ifm_count != 0) 337 free(kptr, M_TEMP); 338 339 ifmr->ifm_count = count; 340 break; 341 } 342 343 default: 344 return (EINVAL); 345 } 346 347 return (error); 348 } 349 350 /* 351 * Find media entry matching a given ifm word. 352 * 353 */ 354 static struct ifmedia_entry * 355 ifmedia_match(ifm, target, mask) 356 struct ifmedia *ifm; 357 int target; 358 int mask; 359 { 360 struct ifmedia_entry *match, *next; 361 362 match = NULL; 363 mask = ~mask; 364 365 LIST_FOREACH(next, &ifm->ifm_list, ifm_list) { 366 if ((next->ifm_media & mask) == (target & mask)) { 367 #if defined(IFMEDIA_DEBUG) || defined(DIAGNOSTIC) 368 if (match) { 369 printf("ifmedia_match: multiple match for " 370 "0x%x/0x%x\n", target, mask); 371 } 372 #endif 373 match = next; 374 } 375 } 376 377 return match; 378 } 379 380 #ifdef IFMEDIA_DEBUG 381 struct ifmedia_description ifm_type_descriptions[] = 382 IFM_TYPE_DESCRIPTIONS; 383 384 struct ifmedia_description ifm_subtype_ethernet_descriptions[] = 385 IFM_SUBTYPE_ETHERNET_DESCRIPTIONS; 386 387 struct ifmedia_description ifm_subtype_ethernet_option_descriptions[] = 388 IFM_SUBTYPE_ETHERNET_OPTION_DESCRIPTIONS; 389 390 struct ifmedia_description ifm_subtype_tokenring_descriptions[] = 391 IFM_SUBTYPE_TOKENRING_DESCRIPTIONS; 392 393 struct ifmedia_description ifm_subtype_tokenring_option_descriptions[] = 394 IFM_SUBTYPE_TOKENRING_OPTION_DESCRIPTIONS; 395 396 struct ifmedia_description ifm_subtype_fddi_descriptions[] = 397 IFM_SUBTYPE_FDDI_DESCRIPTIONS; 398 399 struct ifmedia_description ifm_subtype_fddi_option_descriptions[] = 400 IFM_SUBTYPE_FDDI_OPTION_DESCRIPTIONS; 401 402 struct ifmedia_description ifm_subtype_ieee80211_descriptions[] = 403 IFM_SUBTYPE_IEEE80211_DESCRIPTIONS; 404 405 struct ifmedia_description ifm_subtype_ieee80211_option_descriptions[] = 406 IFM_SUBTYPE_IEEE80211_OPTION_DESCRIPTIONS; 407 408 struct ifmedia_description ifm_subtype_shared_descriptions[] = 409 IFM_SUBTYPE_SHARED_DESCRIPTIONS; 410 411 struct ifmedia_description ifm_shared_option_descriptions[] = 412 IFM_SHARED_OPTION_DESCRIPTIONS; 413 414 struct ifmedia_type_to_subtype { 415 struct ifmedia_description *subtypes; 416 struct ifmedia_description *options; 417 }; 418 419 /* must be in the same order as IFM_TYPE_DESCRIPTIONS */ 420 struct ifmedia_type_to_subtype ifmedia_types_to_subtypes[] = { 421 { 422 &ifm_subtype_ethernet_descriptions[0], 423 &ifm_subtype_ethernet_option_descriptions[0] 424 }, 425 { 426 &ifm_subtype_tokenring_descriptions[0], 427 &ifm_subtype_tokenring_option_descriptions[0] 428 }, 429 { 430 &ifm_subtype_fddi_descriptions[0], 431 &ifm_subtype_fddi_option_descriptions[0] 432 }, 433 { 434 &ifm_subtype_ieee80211_descriptions[0], 435 &ifm_subtype_ieee80211_option_descriptions[0] 436 }, 437 }; 438 439 /* 440 * print a media word. 441 */ 442 static void 443 ifmedia_printword(ifmw) 444 int ifmw; 445 { 446 struct ifmedia_description *desc; 447 struct ifmedia_type_to_subtype *ttos; 448 int seen_option = 0; 449 450 /* Find the top-level interface type. */ 451 for (desc = ifm_type_descriptions, ttos = ifmedia_types_to_subtypes; 452 desc->ifmt_string != NULL; desc++, ttos++) 453 if (IFM_TYPE(ifmw) == desc->ifmt_word) 454 break; 455 if (desc->ifmt_string == NULL) { 456 printf("<unknown type>\n"); 457 return; 458 } 459 printf(desc->ifmt_string); 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