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 int ifmedia_debug = 0;
77 SYSCTL_INT(_debug, OID_AUTO, ifmedia, CTLFLAG_RW, &ifmedia_debug,
78 0, "if_media debugging msgs");
79 static void ifmedia_printword(int);
80 #endif
81
82 /*
83 * Initialize if_media struct for a specific interface instance.
84 */
85 void
ifmedia_init(struct ifmedia * ifm,int dontcare_mask,ifm_change_cb_t change_callback,ifm_stat_cb_t status_callback)86 ifmedia_init(struct ifmedia *ifm, int dontcare_mask,
87 ifm_change_cb_t change_callback, ifm_stat_cb_t status_callback)
88 {
89
90 LIST_INIT(&ifm->ifm_list);
91 ifm->ifm_cur = NULL;
92 ifm->ifm_media = 0;
93 ifm->ifm_mask = dontcare_mask; /* IF don't-care bits */
94 ifm->ifm_change = change_callback;
95 ifm->ifm_status = status_callback;
96 }
97
98 void
ifmedia_removeall(struct ifmedia * ifm)99 ifmedia_removeall(struct ifmedia *ifm)
100 {
101 struct ifmedia_entry *entry;
102
103 while ((entry = LIST_FIRST(&ifm->ifm_list)) != NULL) {
104 LIST_REMOVE(entry, ifm_list);
105 free(entry, M_IFADDR);
106 }
107 ifm->ifm_cur = NULL;
108 }
109
110 /*
111 * Add a media configuration to the list of supported media
112 * for a specific interface instance.
113 */
114 void
ifmedia_add(struct ifmedia * ifm,int mword,int data,void * aux)115 ifmedia_add(struct ifmedia *ifm, int mword, int data, 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 (%#010x) ", mword);
126 ifmedia_printword(mword);
127 }
128 #endif
129
130 entry = malloc(sizeof(*entry), M_IFADDR, M_NOWAIT);
131 if (entry == NULL)
132 panic("ifmedia_add: can't malloc entry");
133
134 entry->ifm_media = mword;
135 entry->ifm_data = data;
136 entry->ifm_aux = aux;
137
138 LIST_INSERT_HEAD(&ifm->ifm_list, entry, ifm_list);
139 }
140
141 /*
142 * Add an array of media configurations to the list of
143 * supported media for a specific interface instance.
144 */
145 void
ifmedia_list_add(struct ifmedia * ifm,struct ifmedia_entry * lp,int count)146 ifmedia_list_add(struct ifmedia *ifm, struct ifmedia_entry *lp, 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
ifmedia_set(struct ifmedia * ifm,int target)163 ifmedia_set(struct ifmedia *ifm, int target)
164 {
165 struct ifmedia_entry *match;
166
167 match = ifmedia_match(ifm, target, ifm->ifm_mask);
168
169 if (match == NULL) {
170 printf("ifmedia_set: no match for 0x%x/0x%x\n",
171 target, ~ifm->ifm_mask);
172 panic("ifmedia_set");
173 }
174 ifm->ifm_cur = match;
175
176 #ifdef IFMEDIA_DEBUG
177 if (ifmedia_debug) {
178 printf("ifmedia_set: target ");
179 ifmedia_printword(target);
180 printf("ifmedia_set: setting to ");
181 ifmedia_printword(ifm->ifm_cur->ifm_media);
182 }
183 #endif
184 }
185
186 /*
187 * Given a media word, return one suitable for an application
188 * using the original encoding.
189 */
190 static int
compat_media(int media)191 compat_media(int media)
192 {
193
194 if (IFM_TYPE(media) == IFM_ETHER && IFM_SUBTYPE(media) > IFM_OTHER) {
195 media &= ~(IFM_ETH_XTYPE|IFM_TMASK);
196 media |= IFM_OTHER;
197 }
198 return (media);
199 }
200
201 /*
202 * Device-independent media ioctl support function.
203 */
204 int
ifmedia_ioctl(struct ifnet * ifp,struct ifreq * ifr,struct ifmedia * ifm,u_long cmd)205 ifmedia_ioctl(struct ifnet *ifp, struct ifreq *ifr, struct ifmedia *ifm,
206 u_long cmd)
207 {
208 struct ifmedia_entry *match;
209 struct ifmediareq *ifmr = (struct ifmediareq *) ifr;
210 int error = 0;
211
212 if (ifp == NULL || ifr == NULL || ifm == NULL)
213 return (EINVAL);
214
215 switch (cmd) {
216 /*
217 * Set the current media.
218 */
219 case SIOCSIFMEDIA:
220 {
221 struct ifmedia_entry *oldentry;
222 int oldmedia;
223 int newmedia = ifr->ifr_media;
224
225 match = ifmedia_match(ifm, newmedia, ifm->ifm_mask);
226 if (match == NULL) {
227 #ifdef IFMEDIA_DEBUG
228 if (ifmedia_debug) {
229 printf(
230 "ifmedia_ioctl: no media found for %#010x mask %#010x\n",
231 newmedia, ifm->ifm_mask);
232 }
233 #endif
234 return (ENXIO);
235 }
236
237 /*
238 * If no change, we're done.
239 * XXX Automedia may invole software intervention.
240 * Keep going in case the connected media changed.
241 * Similarly, if best match changed (kernel debugger?).
242 */
243 if (IFM_SUBTYPE(newmedia) != IFM_AUTO &&
244 newmedia == ifm->ifm_media && match == ifm->ifm_cur)
245 return (0);
246
247 /*
248 * We found a match, now make the driver switch to it.
249 * Make sure to preserve our old media type in case the
250 * driver can't switch.
251 */
252 #ifdef IFMEDIA_DEBUG
253 if (ifmedia_debug) {
254 printf("ifmedia_ioctl: switching %s to ",
255 ifp->if_xname);
256 ifmedia_printword(match->ifm_media);
257 }
258 #endif
259 oldentry = ifm->ifm_cur;
260 oldmedia = ifm->ifm_media;
261 ifm->ifm_cur = match;
262 ifm->ifm_media = newmedia;
263 error = (*ifm->ifm_change)(ifp);
264 if (error) {
265 ifm->ifm_cur = oldentry;
266 ifm->ifm_media = oldmedia;
267 }
268 break;
269 }
270
271 /*
272 * Get list of available media and current media on interface.
273 */
274 case SIOCGIFMEDIA:
275 case SIOCGIFXMEDIA:
276 {
277 struct ifmedia_entry *ep;
278 int i;
279
280 if (ifmr->ifm_count < 0)
281 return (EINVAL);
282
283 if (cmd == SIOCGIFMEDIA) {
284 ifmr->ifm_active = ifmr->ifm_current = ifm->ifm_cur ?
285 compat_media(ifm->ifm_cur->ifm_media) : IFM_NONE;
286 } else {
287 ifmr->ifm_active = ifmr->ifm_current = ifm->ifm_cur ?
288 ifm->ifm_cur->ifm_media : IFM_NONE;
289 }
290 ifmr->ifm_mask = ifm->ifm_mask;
291 ifmr->ifm_status = 0;
292 (*ifm->ifm_status)(ifp, ifmr);
293
294 /*
295 * If there are more interfaces on the list, count
296 * them. This allows the caller to set ifmr->ifm_count
297 * to 0 on the first call to know how much space to
298 * allocate.
299 */
300 i = 0;
301 LIST_FOREACH(ep, &ifm->ifm_list, ifm_list) {
302 if (i < ifmr->ifm_count) {
303 error = copyout(&ep->ifm_media,
304 ifmr->ifm_ulist + i, sizeof(int));
305 if (error != 0)
306 break;
307 }
308 i++;
309 }
310 if (error == 0 && i > ifmr->ifm_count)
311 error = ifmr->ifm_count != 0 ? E2BIG : 0;
312 ifmr->ifm_count = i;
313 break;
314 }
315
316 default:
317 return (EINVAL);
318 }
319
320 return (error);
321 }
322
323 /*
324 * Find media entry matching a given ifm word.
325 *
326 */
327 static struct ifmedia_entry *
ifmedia_match(struct ifmedia * ifm,int target,int mask)328 ifmedia_match(struct ifmedia *ifm, int target, int mask)
329 {
330 struct ifmedia_entry *match, *next;
331
332 match = NULL;
333 mask = ~mask;
334
335 LIST_FOREACH(next, &ifm->ifm_list, ifm_list) {
336 if ((next->ifm_media & mask) == (target & mask)) {
337 #if defined(IFMEDIA_DEBUG) || defined(DIAGNOSTIC)
338 if (match) {
339 printf("ifmedia_match: multiple match for "
340 "%#010x/%#010x\n", target, mask);
341 }
342 #endif
343 match = next;
344 }
345 }
346
347 return (match);
348 }
349
350 /*
351 * Compute the interface `baudrate' from the media, for the interface
352 * metrics (used by routing daemons).
353 */
354 static const struct ifmedia_baudrate ifmedia_baudrate_descriptions[] =
355 IFM_BAUDRATE_DESCRIPTIONS;
356
357 uint64_t
ifmedia_baudrate(int mword)358 ifmedia_baudrate(int mword)
359 {
360 int i;
361
362 for (i = 0; ifmedia_baudrate_descriptions[i].ifmb_word != 0; i++) {
363 if (IFM_TYPE_MATCH(mword, ifmedia_baudrate_descriptions[i].
364 ifmb_word))
365 return (ifmedia_baudrate_descriptions[i].ifmb_baudrate);
366 }
367
368 /* Not known. */
369 return (0);
370 }
371
372 #ifdef IFMEDIA_DEBUG
373 static const struct ifmedia_description ifm_type_descriptions[] =
374 IFM_TYPE_DESCRIPTIONS;
375
376 static const struct ifmedia_description ifm_subtype_ethernet_descriptions[] =
377 IFM_SUBTYPE_ETHERNET_DESCRIPTIONS;
378
379 static const struct ifmedia_description
380 ifm_subtype_ethernet_option_descriptions[] =
381 IFM_SUBTYPE_ETHERNET_OPTION_DESCRIPTIONS;
382
383 static const struct ifmedia_description ifm_subtype_ieee80211_descriptions[] =
384 IFM_SUBTYPE_IEEE80211_DESCRIPTIONS;
385
386 static const struct ifmedia_description
387 ifm_subtype_ieee80211_option_descriptions[] =
388 IFM_SUBTYPE_IEEE80211_OPTION_DESCRIPTIONS;
389
390 static const struct ifmedia_description
391 ifm_subtype_ieee80211_mode_descriptions[] =
392 IFM_SUBTYPE_IEEE80211_MODE_DESCRIPTIONS;
393
394 static const struct ifmedia_description ifm_subtype_atm_descriptions[] =
395 IFM_SUBTYPE_ATM_DESCRIPTIONS;
396
397 static const struct ifmedia_description ifm_subtype_atm_option_descriptions[] =
398 IFM_SUBTYPE_ATM_OPTION_DESCRIPTIONS;
399
400 static const struct ifmedia_description ifm_subtype_shared_descriptions[] =
401 IFM_SUBTYPE_SHARED_DESCRIPTIONS;
402
403 static const struct ifmedia_description ifm_shared_option_descriptions[] =
404 IFM_SHARED_OPTION_DESCRIPTIONS;
405
406 struct ifmedia_type_to_subtype {
407 const struct ifmedia_description *subtypes;
408 const struct ifmedia_description *options;
409 const struct ifmedia_description *modes;
410 };
411
412 /* must be in the same order as IFM_TYPE_DESCRIPTIONS */
413 static const struct ifmedia_type_to_subtype ifmedia_types_to_subtypes[] = {
414 {
415 &ifm_subtype_ethernet_descriptions[0],
416 &ifm_subtype_ethernet_option_descriptions[0],
417 NULL,
418 },
419 {
420 &ifm_subtype_ieee80211_descriptions[0],
421 &ifm_subtype_ieee80211_option_descriptions[0],
422 &ifm_subtype_ieee80211_mode_descriptions[0]
423 },
424 {
425 &ifm_subtype_atm_descriptions[0],
426 &ifm_subtype_atm_option_descriptions[0],
427 NULL,
428 },
429 };
430
431 /*
432 * print a media word.
433 */
434 static void
ifmedia_printword(int ifmw)435 ifmedia_printword(int ifmw)
436 {
437 const struct ifmedia_description *desc;
438 const struct ifmedia_type_to_subtype *ttos;
439 int seen_option = 0;
440
441 /* Find the top-level interface type. */
442 for (desc = ifm_type_descriptions, ttos = ifmedia_types_to_subtypes;
443 desc->ifmt_string != NULL; desc++, ttos++)
444 if (IFM_TYPE(ifmw) == desc->ifmt_word)
445 break;
446 if (desc->ifmt_string == NULL) {
447 printf("<unknown type>\n");
448 return;
449 }
450 printf("%s", desc->ifmt_string);
451
452 /* Any mode. */
453 for (desc = ttos->modes; desc && desc->ifmt_string != NULL; desc++)
454 if (IFM_MODE(ifmw) == desc->ifmt_word) {
455 if (desc->ifmt_string != NULL)
456 printf(" mode %s", desc->ifmt_string);
457 break;
458 }
459
460 /*
461 * Check for the shared subtype descriptions first, then the
462 * type-specific ones.
463 */
464 for (desc = ifm_subtype_shared_descriptions;
465 desc->ifmt_string != NULL; desc++)
466 if (IFM_SUBTYPE(ifmw) == desc->ifmt_word)
467 goto got_subtype;
468
469 for (desc = ttos->subtypes; desc->ifmt_string != NULL; desc++)
470 if (IFM_SUBTYPE(ifmw) == desc->ifmt_word)
471 break;
472 if (desc->ifmt_string == NULL) {
473 printf(" <unknown subtype>\n");
474 return;
475 }
476
477 got_subtype:
478 printf(" %s", desc->ifmt_string);
479
480 /*
481 * Look for shared options.
482 */
483 for (desc = ifm_shared_option_descriptions;
484 desc->ifmt_string != NULL; desc++) {
485 if (ifmw & desc->ifmt_word) {
486 if (seen_option == 0)
487 printf(" <");
488 printf("%s%s", seen_option++ ? "," : "",
489 desc->ifmt_string);
490 }
491 }
492
493 /*
494 * Look for subtype-specific options.
495 */
496 for (desc = ttos->options; desc->ifmt_string != NULL; desc++) {
497 if (ifmw & desc->ifmt_word) {
498 if (seen_option == 0)
499 printf(" <");
500 printf("%s%s", seen_option++ ? "," : "",
501 desc->ifmt_string);
502 }
503 }
504 printf("%s\n", seen_option ? ">" : "");
505 }
506 #endif /* IFMEDIA_DEBUG */
507