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