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