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