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