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