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