1 1.54 thorpej /* $NetBSD: if_media.c,v 1.54 2022/09/03 02:47:59 thorpej Exp $ */ 2 1.2 thorpej 3 1.2 thorpej /*- 4 1.52 thorpej * Copyright (c) 1998, 2020 The NetBSD Foundation, Inc. 5 1.2 thorpej * All rights reserved. 6 1.2 thorpej * 7 1.2 thorpej * This code is derived from software contributed to The NetBSD Foundation 8 1.2 thorpej * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility, 9 1.2 thorpej * NASA Ames Research Center. 10 1.2 thorpej * 11 1.2 thorpej * Redistribution and use in source and binary forms, with or without 12 1.2 thorpej * modification, are permitted provided that the following conditions 13 1.2 thorpej * are met: 14 1.2 thorpej * 1. Redistributions of source code must retain the above copyright 15 1.2 thorpej * notice, this list of conditions and the following disclaimer. 16 1.2 thorpej * 2. Redistributions in binary form must reproduce the above copyright 17 1.2 thorpej * notice, this list of conditions and the following disclaimer in the 18 1.2 thorpej * documentation and/or other materials provided with the distribution. 19 1.2 thorpej * 20 1.2 thorpej * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 21 1.2 thorpej * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 22 1.2 thorpej * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 23 1.2 thorpej * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 24 1.2 thorpej * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 25 1.2 thorpej * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 26 1.2 thorpej * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 27 1.2 thorpej * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 28 1.2 thorpej * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 29 1.2 thorpej * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 30 1.2 thorpej * POSSIBILITY OF SUCH DAMAGE. 31 1.2 thorpej */ 32 1.1 thorpej 33 1.1 thorpej /* 34 1.1 thorpej * Copyright (c) 1997 35 1.1 thorpej * Jonathan Stone and Jason R. Thorpe. All rights reserved. 36 1.1 thorpej * 37 1.1 thorpej * This software is derived from information provided by Matt Thomas. 38 1.1 thorpej * 39 1.1 thorpej * Redistribution and use in source and binary forms, with or without 40 1.1 thorpej * modification, are permitted provided that the following conditions 41 1.1 thorpej * are met: 42 1.1 thorpej * 1. Redistributions of source code must retain the above copyright 43 1.1 thorpej * notice, this list of conditions and the following disclaimer. 44 1.1 thorpej * 2. Redistributions in binary form must reproduce the above copyright 45 1.1 thorpej * notice, this list of conditions and the following disclaimer in the 46 1.1 thorpej * documentation and/or other materials provided with the distribution. 47 1.1 thorpej * 3. All advertising materials mentioning features or use of this software 48 1.1 thorpej * must display the following acknowledgement: 49 1.1 thorpej * This product includes software developed by Jonathan Stone 50 1.1 thorpej * and Jason R. Thorpe for the NetBSD Project. 51 1.1 thorpej * 4. The names of the authors may not be used to endorse or promote products 52 1.1 thorpej * derived from this software without specific prior written permission. 53 1.1 thorpej * 54 1.1 thorpej * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR 55 1.1 thorpej * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 56 1.1 thorpej * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 57 1.1 thorpej * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 58 1.1 thorpej * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 59 1.1 thorpej * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 60 1.1 thorpej * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 61 1.1 thorpej * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 62 1.1 thorpej * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 63 1.1 thorpej * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 64 1.1 thorpej * SUCH DAMAGE. 65 1.1 thorpej */ 66 1.1 thorpej 67 1.1 thorpej /* 68 1.1 thorpej * BSD/OS-compatible network interface media selection. 69 1.1 thorpej * 70 1.1 thorpej * Where it is safe to do so, this code strays slightly from the BSD/OS 71 1.1 thorpej * design. Software which uses the API (device drivers, basically) 72 1.1 thorpej * shouldn't notice any difference. 73 1.1 thorpej * 74 1.1 thorpej * Many thanks to Matt Thomas for providing the information necessary 75 1.1 thorpej * to implement this interface. 76 1.1 thorpej */ 77 1.15 lukem 78 1.15 lukem #include <sys/cdefs.h> 79 1.54 thorpej __KERNEL_RCSID(0, "$NetBSD: if_media.c,v 1.54 2022/09/03 02:47:59 thorpej Exp $"); 80 1.52 thorpej 81 1.52 thorpej #define __IFMEDIA_PRIVATE 82 1.1 thorpej 83 1.1 thorpej #include <sys/param.h> 84 1.1 thorpej #include <sys/systm.h> 85 1.1 thorpej #include <sys/errno.h> 86 1.1 thorpej #include <sys/ioctl.h> 87 1.1 thorpej #include <sys/socket.h> 88 1.50 thorpej #include <sys/kmem.h> 89 1.1 thorpej 90 1.1 thorpej #include <net/if.h> 91 1.1 thorpej #include <net/if_media.h> 92 1.1 thorpej 93 1.40 msaitoh static void ifmedia_status(struct ifmedia *, struct ifnet *, 94 1.52 thorpej struct ifmediareq *); 95 1.52 thorpej static struct ifmedia_entry * 96 1.52 thorpej ifmedia_match_locked(struct ifmedia *, u_int, u_int); 97 1.36 mlelstv 98 1.1 thorpej /* 99 1.1 thorpej * Compile-time options: 100 1.1 thorpej * IFMEDIA_DEBUG: 101 1.43 msaitoh * Turn on implementation-level debug printfs. 102 1.43 msaitoh * Useful for debugging newly-ported drivers. 103 1.1 thorpej */ 104 1.1 thorpej 105 1.1 thorpej #ifdef IFMEDIA_DEBUG 106 1.1 thorpej int ifmedia_debug = 0; 107 1.22 thorpej static void ifmedia_printword(int); 108 1.1 thorpej #endif 109 1.1 thorpej 110 1.1 thorpej /* 111 1.52 thorpej * We need to implement a recursive mutex to handle the un-converted 112 1.52 thorpej * driver case. For a fully MP-safe driver, the media lock will be 113 1.52 thorpej * held before calling any of the entry points that require it. However, 114 1.52 thorpej * this is not necessarily the case for a driver that hasn't yet been 115 1.52 thorpej * converted, and the entry point calls may be nested (for example 116 1.52 thorpej * mii_ifmedia_change -> ether_mediachange -> mii_mediachg). Luckily, 117 1.52 thorpej * the nesting won't be very deep, and 4 nested holds should be plenty. 118 1.52 thorpej */ 119 1.52 thorpej #define IFM_L_OWNLOCK 0x01 120 1.52 thorpej #define IFM_L_COUNT_MASK 0x3UL 121 1.52 thorpej #define IFM_L_CPU_MASK ~(IFM_L_COUNT_MASK) 122 1.52 thorpej 123 1.52 thorpej void 124 1.52 thorpej ifmedia_lock_for_legacy(struct ifmedia *ifm) 125 1.52 thorpej { 126 1.52 thorpej uintptr_t cnt = IFM_L_OWNLOCK; 127 1.52 thorpej uintptr_t ci; 128 1.52 thorpej 129 1.52 thorpej if (mutex_tryenter(ifm->ifm_lock)) { 130 1.52 thorpej goto gotit; 131 1.52 thorpej } 132 1.52 thorpej 133 1.52 thorpej kpreempt_disable(); 134 1.52 thorpej ci = (uintptr_t)curcpu(); 135 1.52 thorpej if ((ifm->ifm_legacy & IFM_L_CPU_MASK) == ci) { 136 1.52 thorpej cnt = ifm->ifm_legacy & IFM_L_COUNT_MASK; 137 1.52 thorpej KASSERT(cnt < IFM_L_COUNT_MASK); 138 1.52 thorpej cnt++; 139 1.52 thorpej kpreempt_enable(); 140 1.52 thorpej goto gotit; 141 1.52 thorpej } 142 1.52 thorpej kpreempt_enable(); 143 1.52 thorpej 144 1.52 thorpej mutex_enter(ifm->ifm_lock); 145 1.52 thorpej gotit: 146 1.52 thorpej KASSERT(kpreempt_disabled()); 147 1.52 thorpej ci = (uintptr_t)curcpu(); 148 1.52 thorpej KASSERT((ci & IFM_L_CPU_MASK) == ci); 149 1.52 thorpej ifm->ifm_legacy = ci | cnt; 150 1.52 thorpej } 151 1.52 thorpej 152 1.52 thorpej void 153 1.52 thorpej ifmedia_unlock_for_legacy(struct ifmedia *ifm) 154 1.52 thorpej { 155 1.52 thorpej uintptr_t cnt; 156 1.52 thorpej uintptr_t ci = (uintptr_t)curcpu(); 157 1.52 thorpej 158 1.52 thorpej KASSERT(kpreempt_disabled()); 159 1.52 thorpej KASSERT((ifm->ifm_legacy & IFM_L_CPU_MASK) == ci); 160 1.52 thorpej cnt = ifm->ifm_legacy & IFM_L_COUNT_MASK; 161 1.52 thorpej KASSERT(cnt != 0); 162 1.52 thorpej if (cnt == IFM_L_OWNLOCK) { 163 1.52 thorpej ifm->ifm_legacy = IFM_L_OWNLOCK; 164 1.52 thorpej mutex_exit(ifm->ifm_lock); 165 1.52 thorpej return; 166 1.52 thorpej } 167 1.52 thorpej cnt--; 168 1.52 thorpej ifm->ifm_legacy = ci | cnt; 169 1.52 thorpej } 170 1.52 thorpej 171 1.52 thorpej /* 172 1.1 thorpej * Initialize if_media struct for a specific interface instance. 173 1.1 thorpej */ 174 1.1 thorpej void 175 1.52 thorpej ifmedia_init_with_lock(struct ifmedia *ifm, int dontcare_mask, 176 1.52 thorpej ifm_change_cb_t change_callback, ifm_stat_cb_t status_callback, 177 1.52 thorpej kmutex_t *lock) 178 1.1 thorpej { 179 1.1 thorpej 180 1.52 thorpej /* 181 1.52 thorpej * XXX Would really like to assert: 182 1.52 thorpej * 183 1.52 thorpej * !if_is_mpsafe(ifp) || ((if_is_mpsafe(ifp) && lock != NULL) 184 1.52 thorpej * 185 1.53 andvar * ...but we don't have access to the ifnet here. 186 1.52 thorpej */ 187 1.52 thorpej 188 1.7 thorpej TAILQ_INIT(&ifm->ifm_list); 189 1.1 thorpej ifm->ifm_cur = NULL; 190 1.31 msaitoh ifm->ifm_media = IFM_NONE; 191 1.1 thorpej ifm->ifm_mask = dontcare_mask; /* IF don't-care bits */ 192 1.1 thorpej ifm->ifm_change = change_callback; 193 1.1 thorpej ifm->ifm_status = status_callback; 194 1.52 thorpej ifm->ifm_legacy = 0; 195 1.52 thorpej 196 1.52 thorpej if (lock == NULL) { 197 1.52 thorpej /* 198 1.52 thorpej * This is to support drivers that are not yet MP-safe 199 1.52 thorpej * with regard to the ifmedia layer. In these cases, 200 1.52 thorpej * we supply the lock and we ensure it's taken upon entry 201 1.52 thorpej * to various routines that expect it to be held. When 202 1.52 thorpej * we do this, we expect that the driver is in general a 203 1.52 thorpej * non-MP-safe driver and has already gone to splnet(). 204 1.52 thorpej */ 205 1.52 thorpej lock = mutex_obj_alloc(MUTEX_DEFAULT, IPL_NET); 206 1.52 thorpej ifm->ifm_legacy = IFM_L_OWNLOCK; 207 1.52 thorpej } 208 1.52 thorpej ifm->ifm_lock = lock; 209 1.52 thorpej } 210 1.52 thorpej 211 1.52 thorpej void 212 1.52 thorpej ifmedia_init(struct ifmedia *ifm, int dontcare_mask, 213 1.52 thorpej ifm_change_cb_t change_callback, ifm_stat_cb_t status_callback) 214 1.52 thorpej { 215 1.52 thorpej ifmedia_init_with_lock(ifm, dontcare_mask, change_callback, 216 1.52 thorpej status_callback, NULL); 217 1.1 thorpej } 218 1.1 thorpej 219 1.51 thorpej /* 220 1.51 thorpej * Free resources associated with an ifmedia. 221 1.51 thorpej */ 222 1.51 thorpej void 223 1.51 thorpej ifmedia_fini(struct ifmedia *ifm) 224 1.51 thorpej { 225 1.51 thorpej 226 1.51 thorpej ifmedia_removeall(ifm); 227 1.52 thorpej 228 1.52 thorpej if (ifm->ifm_legacy) { 229 1.52 thorpej KASSERT(ifm->ifm_legacy == IFM_L_OWNLOCK); 230 1.52 thorpej mutex_obj_free(ifm->ifm_lock); 231 1.52 thorpej } 232 1.52 thorpej ifm->ifm_legacy = 0; 233 1.52 thorpej ifm->ifm_lock = NULL; 234 1.51 thorpej } 235 1.51 thorpej 236 1.27 dyoung int 237 1.27 dyoung ifmedia_change(struct ifmedia *ifm, struct ifnet *ifp) 238 1.27 dyoung { 239 1.52 thorpej int rv; 240 1.52 thorpej 241 1.52 thorpej IFMEDIA_LOCK_FOR_LEGACY(ifm); 242 1.52 thorpej KASSERT(ifmedia_locked(ifm)); 243 1.52 thorpej if (ifm->ifm_change) 244 1.52 thorpej rv = (*ifm->ifm_change)(ifp); 245 1.52 thorpej else 246 1.52 thorpej rv = -1; 247 1.52 thorpej IFMEDIA_UNLOCK_FOR_LEGACY(ifm); 248 1.36 mlelstv 249 1.52 thorpej return rv; 250 1.27 dyoung } 251 1.27 dyoung 252 1.36 mlelstv static void 253 1.46 msaitoh ifmedia_status(struct ifmedia *ifm, struct ifnet *ifp, struct ifmediareq *ifmr) 254 1.36 mlelstv { 255 1.36 mlelstv 256 1.52 thorpej KASSERT(ifmedia_locked(ifm)); 257 1.36 mlelstv if (ifm->ifm_status == NULL) 258 1.36 mlelstv return; 259 1.36 mlelstv (*ifm->ifm_status)(ifp, ifmr); 260 1.36 mlelstv } 261 1.36 mlelstv 262 1.1 thorpej /* 263 1.1 thorpej * Add a media configuration to the list of supported media 264 1.1 thorpej * for a specific interface instance. 265 1.1 thorpej */ 266 1.52 thorpej static void 267 1.52 thorpej ifmedia_add_entry(struct ifmedia *ifm, int mword, int data, void *aux, 268 1.52 thorpej struct ifmedia_entry *entry) 269 1.1 thorpej { 270 1.1 thorpej 271 1.1 thorpej #ifdef IFMEDIA_DEBUG 272 1.1 thorpej if (ifmedia_debug) { 273 1.1 thorpej if (ifm == NULL) { 274 1.1 thorpej printf("ifmedia_add: null ifm\n"); 275 1.1 thorpej return; 276 1.1 thorpej } 277 1.1 thorpej printf("Adding entry for "); 278 1.1 thorpej ifmedia_printword(mword); 279 1.1 thorpej } 280 1.1 thorpej #endif 281 1.1 thorpej 282 1.1 thorpej entry->ifm_media = mword; 283 1.1 thorpej entry->ifm_data = data; 284 1.1 thorpej entry->ifm_aux = aux; 285 1.7 thorpej TAILQ_INSERT_TAIL(&ifm->ifm_list, entry, ifm_list); 286 1.1 thorpej } 287 1.1 thorpej 288 1.52 thorpej void 289 1.52 thorpej ifmedia_add(struct ifmedia *ifm, int mword, int data, void *aux) 290 1.52 thorpej { 291 1.52 thorpej struct ifmedia_entry *entry; 292 1.52 thorpej 293 1.52 thorpej entry = kmem_zalloc(sizeof(*entry), KM_SLEEP); 294 1.52 thorpej ifmedia_lock(ifm); 295 1.52 thorpej ifmedia_add_entry(ifm, mword, data, aux, entry); 296 1.52 thorpej ifmedia_unlock(ifm); 297 1.52 thorpej } 298 1.52 thorpej 299 1.1 thorpej /* 300 1.1 thorpej * Add an array of media configurations to the list of 301 1.1 thorpej * supported media for a specific interface instance. 302 1.1 thorpej */ 303 1.1 thorpej void 304 1.22 thorpej ifmedia_list_add(struct ifmedia *ifm, struct ifmedia_entry *lp, int count) 305 1.1 thorpej { 306 1.1 thorpej int i; 307 1.1 thorpej 308 1.1 thorpej for (i = 0; i < count; i++) 309 1.1 thorpej ifmedia_add(ifm, lp[i].ifm_media, lp[i].ifm_data, 310 1.1 thorpej lp[i].ifm_aux); 311 1.1 thorpej } 312 1.1 thorpej 313 1.1 thorpej /* 314 1.24 perry * Set the default active media. 315 1.1 thorpej * 316 1.1 thorpej * Called by device-specific code which is assumed to have already 317 1.1 thorpej * selected the default media in hardware. We do _not_ call the 318 1.1 thorpej * media-change callback. 319 1.1 thorpej */ 320 1.1 thorpej void 321 1.22 thorpej ifmedia_set(struct ifmedia *ifm, int target) 322 1.1 thorpej { 323 1.52 thorpej struct ifmedia_entry *match, *entry = NULL; 324 1.1 thorpej 325 1.52 thorpej ifmedia_lock(ifm); 326 1.52 thorpej match = ifmedia_match_locked(ifm, target, ifm->ifm_mask); 327 1.1 thorpej 328 1.20 briggs /* 329 1.20 briggs * If we didn't find the requested media, then we try to fall 330 1.20 briggs * back to target-type (IFM_ETHER, e.g.) | IFM_NONE. If that's 331 1.20 briggs * not on the list, then we add it and set the media to it. 332 1.20 briggs * 333 1.20 briggs * Since ifmedia_set is almost always called with IFM_AUTO or 334 1.20 briggs * with a known-good media, this really should only occur if we: 335 1.20 briggs * 336 1.20 briggs * a) didn't find any PHYs, or 337 1.20 briggs * b) didn't find an autoselect option on the PHY when the 338 1.20 briggs * parent ethernet driver expected to. 339 1.20 briggs * 340 1.20 briggs * In either case, it makes sense to select no media. 341 1.20 briggs */ 342 1.1 thorpej if (match == NULL) { 343 1.1 thorpej printf("ifmedia_set: no match for 0x%x/0x%x\n", 344 1.1 thorpej target, ~ifm->ifm_mask); 345 1.20 briggs target = (target & IFM_NMASK) | IFM_NONE; 346 1.52 thorpej match = ifmedia_match_locked(ifm, target, ifm->ifm_mask); 347 1.20 briggs if (match == NULL) { 348 1.52 thorpej ifmedia_unlock(ifm); 349 1.52 thorpej entry = kmem_zalloc(sizeof(*entry), KM_SLEEP); 350 1.52 thorpej ifmedia_lock(ifm); 351 1.52 thorpej match = ifmedia_match_locked(ifm, target, 352 1.52 thorpej ifm->ifm_mask); 353 1.52 thorpej if (match == NULL) { 354 1.52 thorpej ifmedia_add_entry(ifm, target, 0, NULL, entry); 355 1.52 thorpej entry = NULL; 356 1.52 thorpej } 357 1.52 thorpej match = ifmedia_match_locked(ifm, target, 358 1.52 thorpej ifm->ifm_mask); 359 1.33 msaitoh if (match == NULL) 360 1.20 briggs panic("ifmedia_set failed"); 361 1.20 briggs } 362 1.1 thorpej } 363 1.1 thorpej ifm->ifm_cur = match; 364 1.52 thorpej ifmedia_unlock(ifm); 365 1.52 thorpej 366 1.52 thorpej if (entry) 367 1.52 thorpej kmem_free(entry, sizeof(*entry)); 368 1.1 thorpej 369 1.1 thorpej #ifdef IFMEDIA_DEBUG 370 1.1 thorpej if (ifmedia_debug) { 371 1.1 thorpej printf("ifmedia_set: target "); 372 1.1 thorpej ifmedia_printword(target); 373 1.1 thorpej printf("ifmedia_set: setting to "); 374 1.1 thorpej ifmedia_printword(ifm->ifm_cur->ifm_media); 375 1.1 thorpej } 376 1.1 thorpej #endif 377 1.1 thorpej } 378 1.1 thorpej 379 1.50 thorpej static int 380 1.50 thorpej ifmedia_getwords(struct ifmedia * const ifm, int *words, int maxwords) 381 1.50 thorpej { 382 1.50 thorpej struct ifmedia_entry *ep; 383 1.50 thorpej int nwords = 0; 384 1.50 thorpej 385 1.52 thorpej KASSERT(ifmedia_locked(ifm)); 386 1.52 thorpej 387 1.50 thorpej TAILQ_FOREACH(ep, &ifm->ifm_list, ifm_list) { 388 1.50 thorpej if (words != NULL && nwords < maxwords) { 389 1.50 thorpej words[nwords] = ep->ifm_media; 390 1.50 thorpej } 391 1.50 thorpej nwords++; 392 1.50 thorpej } 393 1.50 thorpej 394 1.50 thorpej return nwords; 395 1.50 thorpej } 396 1.50 thorpej 397 1.52 thorpej #define IFMEDIA_IOCTL_LOCK(ifm) \ 398 1.52 thorpej do { \ 399 1.52 thorpej if (ifmedia_islegacy(ifm)) \ 400 1.52 thorpej ifmedia_lock_for_legacy(ifm); \ 401 1.52 thorpej else \ 402 1.52 thorpej ifmedia_lock(ifm); \ 403 1.52 thorpej } while (/*CONSTCOND*/0) 404 1.52 thorpej 405 1.52 thorpej #define IFMEDIA_IOCTL_UNLOCK(ifm) \ 406 1.52 thorpej do { \ 407 1.52 thorpej if (ifmedia_islegacy(ifm)) \ 408 1.52 thorpej ifmedia_unlock_for_legacy(ifm); \ 409 1.52 thorpej else \ 410 1.52 thorpej ifmedia_unlock(ifm); \ 411 1.52 thorpej } while (/*CONSTCOND*/0) 412 1.52 thorpej 413 1.1 thorpej /* 414 1.1 thorpej * Device-independent media ioctl support function. 415 1.1 thorpej */ 416 1.52 thorpej int 417 1.52 thorpej ifmedia_ioctl(struct ifnet *ifp, struct ifreq *ifr, struct ifmedia *ifm, 418 1.22 thorpej u_long cmd) 419 1.1 thorpej { 420 1.1 thorpej struct ifmedia_entry *match; 421 1.37 msaitoh struct ifmediareq *ifmr = (struct ifmediareq *)ifr; 422 1.19 christos int error = 0; 423 1.1 thorpej 424 1.1 thorpej if (ifp == NULL || ifr == NULL || ifm == NULL) 425 1.43 msaitoh return EINVAL; 426 1.1 thorpej 427 1.52 thorpej KERNEL_LOCK_UNLESS_IFP_MPSAFE(ifp); 428 1.52 thorpej 429 1.1 thorpej switch (cmd) { 430 1.43 msaitoh case SIOCSIFMEDIA: /* Set the current media. */ 431 1.1 thorpej { 432 1.1 thorpej struct ifmedia_entry *oldentry; 433 1.17 thorpej u_int oldmedia; 434 1.17 thorpej u_int newmedia = ifr->ifr_media; 435 1.1 thorpej 436 1.52 thorpej IFMEDIA_IOCTL_LOCK(ifm); 437 1.52 thorpej 438 1.52 thorpej match = ifmedia_match_locked(ifm, newmedia, ifm->ifm_mask); 439 1.1 thorpej if (match == NULL) { 440 1.1 thorpej #ifdef IFMEDIA_DEBUG 441 1.1 thorpej if (ifmedia_debug) { 442 1.44 msaitoh printf("ifmedia_ioctl: no media found for " 443 1.44 msaitoh "0x%08x\n", newmedia); 444 1.1 thorpej } 445 1.1 thorpej #endif 446 1.52 thorpej IFMEDIA_IOCTL_UNLOCK(ifm); 447 1.52 thorpej error = EINVAL; 448 1.52 thorpej break; 449 1.1 thorpej } 450 1.1 thorpej 451 1.1 thorpej /* 452 1.1 thorpej * If no change, we're done. 453 1.14 drochner * XXX Automedia may involve software intervention. 454 1.10 soren * Keep going in case the connected media changed. 455 1.1 thorpej * Similarly, if best match changed (kernel debugger?). 456 1.1 thorpej */ 457 1.1 thorpej if ((IFM_SUBTYPE(newmedia) != IFM_AUTO) && 458 1.52 thorpej (newmedia == ifm->ifm_media) && (match == ifm->ifm_cur)) { 459 1.52 thorpej IFMEDIA_IOCTL_UNLOCK(ifm); 460 1.52 thorpej break; 461 1.52 thorpej } 462 1.1 thorpej 463 1.1 thorpej /* 464 1.1 thorpej * We found a match, now make the driver switch to it. 465 1.1 thorpej * Make sure to preserve our old media type in case the 466 1.1 thorpej * driver can't switch. 467 1.1 thorpej */ 468 1.1 thorpej #ifdef IFMEDIA_DEBUG 469 1.1 thorpej if (ifmedia_debug) { 470 1.1 thorpej printf("ifmedia_ioctl: switching %s to ", 471 1.1 thorpej ifp->if_xname); 472 1.1 thorpej ifmedia_printword(match->ifm_media); 473 1.1 thorpej } 474 1.1 thorpej #endif 475 1.1 thorpej oldentry = ifm->ifm_cur; 476 1.1 thorpej oldmedia = ifm->ifm_media; 477 1.1 thorpej ifm->ifm_cur = match; 478 1.1 thorpej ifm->ifm_media = newmedia; 479 1.27 dyoung error = ifmedia_change(ifm, ifp); 480 1.1 thorpej if (error) { 481 1.1 thorpej ifm->ifm_cur = oldentry; 482 1.1 thorpej ifm->ifm_media = oldmedia; 483 1.1 thorpej } 484 1.52 thorpej IFMEDIA_IOCTL_UNLOCK(ifm); 485 1.1 thorpej break; 486 1.1 thorpej } 487 1.1 thorpej 488 1.43 msaitoh /* Get list of available media and current media on interface. */ 489 1.24 perry case SIOCGIFMEDIA: 490 1.1 thorpej { 491 1.50 thorpej int nwords1, nwords2; 492 1.1 thorpej 493 1.52 thorpej if (ifmr->ifm_count < 0) { 494 1.52 thorpej error = EINVAL; 495 1.52 thorpej break; 496 1.52 thorpej } 497 1.1 thorpej 498 1.52 thorpej IFMEDIA_IOCTL_LOCK(ifm); 499 1.1 thorpej ifmr->ifm_active = ifmr->ifm_current = ifm->ifm_cur ? 500 1.1 thorpej ifm->ifm_cur->ifm_media : IFM_NONE; 501 1.1 thorpej ifmr->ifm_mask = ifm->ifm_mask; 502 1.1 thorpej ifmr->ifm_status = 0; 503 1.36 mlelstv ifmedia_status(ifm, ifp, ifmr); 504 1.1 thorpej 505 1.19 christos /* 506 1.52 thorpej * Count them so we know how much is the max we'll 507 1.19 christos * need. 508 1.19 christos */ 509 1.50 thorpej nwords1 = nwords2 = ifmedia_getwords(ifm, NULL, 0); 510 1.52 thorpej IFMEDIA_IOCTL_UNLOCK(ifm); 511 1.1 thorpej 512 1.1 thorpej if (ifmr->ifm_count != 0) { 513 1.50 thorpej int maxwords = MIN(nwords1, ifmr->ifm_count); 514 1.50 thorpej int *kptr = kmem_zalloc(maxwords * sizeof(int), 515 1.50 thorpej KM_SLEEP); 516 1.1 thorpej 517 1.52 thorpej ifmedia_lock(ifm); 518 1.50 thorpej nwords2 = ifmedia_getwords(ifm, kptr, maxwords); 519 1.52 thorpej ifmedia_unlock(ifm); 520 1.19 christos error = copyout(kptr, ifmr->ifm_ulist, 521 1.50 thorpej maxwords * sizeof(int)); 522 1.50 thorpej if (error == 0 && nwords2 > nwords1) 523 1.1 thorpej error = E2BIG; /* oops! */ 524 1.50 thorpej kmem_free(kptr, maxwords * sizeof(int)); 525 1.1 thorpej } 526 1.45 msaitoh /* Update with the real number */ 527 1.50 thorpej ifmr->ifm_count = nwords2; 528 1.1 thorpej break; 529 1.1 thorpej } 530 1.1 thorpej 531 1.1 thorpej default: 532 1.52 thorpej error = EINVAL; 533 1.52 thorpej break; 534 1.1 thorpej } 535 1.1 thorpej 536 1.52 thorpej KERNEL_UNLOCK_UNLESS_IFP_MPSAFE(ifp); 537 1.52 thorpej 538 1.33 msaitoh return error; 539 1.1 thorpej } 540 1.1 thorpej 541 1.1 thorpej /* 542 1.1 thorpej * Find media entry matching a given ifm word. 543 1.1 thorpej */ 544 1.52 thorpej static struct ifmedia_entry * 545 1.52 thorpej ifmedia_match_locked(struct ifmedia *ifm, u_int target, u_int mask) 546 1.1 thorpej { 547 1.1 thorpej struct ifmedia_entry *match, *next; 548 1.1 thorpej 549 1.1 thorpej match = NULL; 550 1.1 thorpej mask = ~mask; 551 1.1 thorpej 552 1.37 msaitoh TAILQ_FOREACH(next, &ifm->ifm_list, ifm_list) { 553 1.1 thorpej if ((next->ifm_media & mask) == (target & mask)) { 554 1.18 chs if (match) { 555 1.1 thorpej #if defined(IFMEDIA_DEBUG) || defined(DIAGNOSTIC) 556 1.1 thorpej printf("ifmedia_match: multiple match for " 557 1.18 chs "0x%x/0x%x, selected instance %d\n", 558 1.18 chs target, mask, IFM_INST(match->ifm_media)); 559 1.18 chs #endif 560 1.18 chs break; 561 1.1 thorpej } 562 1.1 thorpej match = next; 563 1.1 thorpej } 564 1.1 thorpej } 565 1.1 thorpej 566 1.1 thorpej return match; 567 1.8 thorpej } 568 1.8 thorpej 569 1.52 thorpej struct ifmedia_entry * 570 1.52 thorpej ifmedia_match(struct ifmedia *ifm, u_int target, u_int mask) 571 1.52 thorpej { 572 1.52 thorpej struct ifmedia_entry *match; 573 1.52 thorpej 574 1.52 thorpej /* 575 1.52 thorpej * N.B. We expect the caller is responsible fot the lifecycle 576 1.52 thorpej * of the media entries. Use with extreme caution. 577 1.52 thorpej */ 578 1.52 thorpej 579 1.52 thorpej ifmedia_lock(ifm); 580 1.52 thorpej match = ifmedia_match_locked(ifm, target, mask); 581 1.52 thorpej ifmedia_unlock(ifm); 582 1.52 thorpej return match; 583 1.52 thorpej } 584 1.52 thorpej 585 1.8 thorpej /* 586 1.8 thorpej * Delete all media for a given instance. 587 1.8 thorpej */ 588 1.8 thorpej void 589 1.22 thorpej ifmedia_delete_instance(struct ifmedia *ifm, u_int inst) 590 1.8 thorpej { 591 1.8 thorpej struct ifmedia_entry *ife, *nife; 592 1.52 thorpej TAILQ_HEAD(, ifmedia_entry) dead_entries; 593 1.52 thorpej 594 1.52 thorpej TAILQ_INIT(&dead_entries); 595 1.8 thorpej 596 1.52 thorpej ifmedia_lock(ifm); 597 1.37 msaitoh TAILQ_FOREACH_SAFE(ife, &ifm->ifm_list, ifm_list, nife) { 598 1.8 thorpej if (inst == IFM_INST_ANY || 599 1.8 thorpej inst == IFM_INST(ife->ifm_media)) { 600 1.51 thorpej if (ifm->ifm_cur == ife) { 601 1.51 thorpej ifm->ifm_cur = NULL; 602 1.51 thorpej ifm->ifm_media = IFM_NONE; 603 1.51 thorpej } 604 1.8 thorpej TAILQ_REMOVE(&ifm->ifm_list, ife, ifm_list); 605 1.52 thorpej TAILQ_INSERT_TAIL(&dead_entries, ife, ifm_list); 606 1.8 thorpej } 607 1.8 thorpej } 608 1.52 thorpej ifmedia_unlock(ifm); 609 1.52 thorpej 610 1.52 thorpej TAILQ_FOREACH_SAFE(ife, &dead_entries, ifm_list, nife) { 611 1.52 thorpej TAILQ_REMOVE(&dead_entries, ife, ifm_list); 612 1.52 thorpej kmem_free(ife, sizeof(*ife)); 613 1.52 thorpej } 614 1.9 thorpej } 615 1.9 thorpej 616 1.29 christos void 617 1.29 christos ifmedia_removeall(struct ifmedia *ifm) 618 1.29 christos { 619 1.29 christos 620 1.33 msaitoh ifmedia_delete_instance(ifm, IFM_INST_ANY); 621 1.29 christos } 622 1.29 christos 623 1.9 thorpej /* 624 1.9 thorpej * Compute the interface `baudrate' from the media, for the interface 625 1.9 thorpej * metrics (used by routing daemons). 626 1.9 thorpej */ 627 1.12 jdolecek static const struct ifmedia_baudrate ifmedia_baudrate_descriptions[] = 628 1.9 thorpej IFM_BAUDRATE_DESCRIPTIONS; 629 1.9 thorpej 630 1.30 dyoung uint64_t 631 1.21 ragge ifmedia_baudrate(int mword) 632 1.9 thorpej { 633 1.9 thorpej int i; 634 1.9 thorpej 635 1.9 thorpej for (i = 0; ifmedia_baudrate_descriptions[i].ifmb_word != 0; i++) { 636 1.45 msaitoh if (IFM_TYPE_SUBTYPE_MATCH(mword, 637 1.45 msaitoh ifmedia_baudrate_descriptions[i].ifmb_word)) 638 1.43 msaitoh return ifmedia_baudrate_descriptions[i].ifmb_baudrate; 639 1.9 thorpej } 640 1.9 thorpej 641 1.9 thorpej /* Not known. */ 642 1.33 msaitoh return 0; 643 1.1 thorpej } 644 1.1 thorpej 645 1.1 thorpej #ifdef IFMEDIA_DEBUG 646 1.2 thorpej 647 1.12 jdolecek static const struct ifmedia_description ifm_type_descriptions[] = 648 1.1 thorpej IFM_TYPE_DESCRIPTIONS; 649 1.1 thorpej 650 1.12 jdolecek static const struct ifmedia_description ifm_subtype_descriptions[] = 651 1.2 thorpej IFM_SUBTYPE_DESCRIPTIONS; 652 1.1 thorpej 653 1.12 jdolecek static const struct ifmedia_description ifm_option_descriptions[] = 654 1.2 thorpej IFM_OPTION_DESCRIPTIONS; 655 1.1 thorpej 656 1.1 thorpej /* 657 1.1 thorpej * print a media word. 658 1.1 thorpej */ 659 1.1 thorpej static void 660 1.22 thorpej ifmedia_printword(int ifmw) 661 1.1 thorpej { 662 1.12 jdolecek const struct ifmedia_description *desc; 663 1.1 thorpej int seen_option = 0; 664 1.1 thorpej 665 1.2 thorpej /* Print the top-level interface type. */ 666 1.2 thorpej for (desc = ifm_type_descriptions; desc->ifmt_string != NULL; 667 1.2 thorpej desc++) { 668 1.1 thorpej if (IFM_TYPE(ifmw) == desc->ifmt_word) 669 1.1 thorpej break; 670 1.1 thorpej } 671 1.2 thorpej if (desc->ifmt_string == NULL) 672 1.2 thorpej printf("<unknown type> "); 673 1.2 thorpej else 674 1.2 thorpej printf("%s ", desc->ifmt_string); 675 1.2 thorpej 676 1.2 thorpej /* Print the subtype. */ 677 1.2 thorpej for (desc = ifm_subtype_descriptions; desc->ifmt_string != NULL; 678 1.2 thorpej desc++) { 679 1.2 thorpej if (IFM_TYPE_MATCH(desc->ifmt_word, ifmw) && 680 1.2 thorpej IFM_SUBTYPE(desc->ifmt_word) == IFM_SUBTYPE(ifmw)) 681 1.1 thorpej break; 682 1.1 thorpej } 683 1.2 thorpej if (desc->ifmt_string == NULL) 684 1.2 thorpej printf("<unknown subtype>"); 685 1.2 thorpej else 686 1.2 thorpej printf("%s", desc->ifmt_string); 687 1.2 thorpej 688 1.2 thorpej /* Print any options. */ 689 1.2 thorpej for (desc = ifm_option_descriptions; desc->ifmt_string != NULL; 690 1.2 thorpej desc++) { 691 1.2 thorpej if (IFM_TYPE_MATCH(desc->ifmt_word, ifmw) && 692 1.2 thorpej (ifmw & desc->ifmt_word) != 0 && 693 1.2 thorpej (seen_option & IFM_OPTIONS(desc->ifmt_word)) == 0) { 694 1.1 thorpej if (seen_option == 0) 695 1.1 thorpej printf(" <"); 696 1.3 enami printf("%s%s", seen_option ? "," : "", 697 1.1 thorpej desc->ifmt_string); 698 1.2 thorpej seen_option |= IFM_OPTIONS(desc->ifmt_word); 699 1.1 thorpej } 700 1.1 thorpej } 701 1.1 thorpej printf("%s\n", seen_option ? ">" : ""); 702 1.1 thorpej } 703 1.2 thorpej 704 1.1 thorpej #endif /* IFMEDIA_DEBUG */ 705