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